Your Roblox Game LAGS (and how to fix it) — Transcript
Full transcript
- 0:00If you want to learn how to optimize
- 0:01your Roblox game, then look no further.
- 0:04In this video, we'll be exploring tons
- 0:06of ways to optimize your game and
- 0:08squeeze out as much performance as
- 0:10possible, especially for your mobile
- 0:12players. And speaking of exploring,
- 0:14let's explore and learn about today's
- 0:16sponsor, Brilliant. Brilliant is an
- 0:19online learning platform designed to
- 0:20make you a better thinker and problem
- 0:23solver. They have thousands of lectures
- 0:25across a variety of subjects such as
- 0:27math, science, programming, data
- 0:29analysis, and artificial intelligence.
- 0:31Each lesson is interactive and contains
- 0:33hands-on problem solving, allowing you
- 0:35to experiment and play with concepts
- 0:37instead of watching boring lecture
- 0:39videos. This style of learning is
- 0:41extremely effective, which allows
- 0:43Brilliant to help you build your
- 0:44understanding from the ground up. You'll
- 0:47start off on the basics of concepts and
- 0:49work your way up as Brilliant keeps
- 0:50track of your progress and places
- 0:52increasingly challenging problems in
- 0:54your way to make sure you're still
- 0:56learning. As programmers, we often
- 0:58neglect to understand the lower level
- 1:00processes of computers and how they
- 1:02transform our code into usable
- 1:04instructions. But thanks to Brilliant's
- 1:07new digital circuits course, you can
- 1:09gain an incredible understanding of how
- 1:11logic gates work, which are one of the
- 1:13fundamental components used to perform
- 1:15logical operations within a processor.
- 1:18I've already started going through the
- 1:19digital circuits course, and I love how
- 1:21incredibly interactive and simple it is
- 1:23to learn about the millions of logic
- 1:25gates working behind the scenes in our
- 1:27everyday devices. To get started with
- 1:29Brilliant, you can go to
- 1:30brilliant.org/crusherfire
- 1:31org/crusherfire or scan the QR code on
- 1:33screen to get 30 days absolutely free.
- 1:36You'll also get 20% off an annual
- 1:38premium subscription. Thanks to
- 1:40Brilliant for sponsoring this video. One
- 1:42of the first areas of optimization we're
- 1:44going to be taking a look at is how to
- 1:45improve the memory usage in your
- 1:47experience. And a really easy way to
- 1:49improve memory usage in your experience
- 1:51is by checking if you have high memory
- 1:53usage regarding collisions in your game.
- 1:56So if we read the Roblox documentation,
- 1:58physics, movement and collision
- 1:59detection consumes memory. Mesh parts
- 2:02along with constructive solid geometry
- 2:04parts have a collision fidelity property
- 2:06that determines the approach that used
- 2:07to evaluate the collision bounds of the
- 2:09mesh. The default and precise collision
- 2:12detection modes consumes significantly
- 2:14more memory than the other two modes
- 2:16with lower fidelity collision shapes. If
- 2:18you see high levels of memory
- 2:20consumption under physics parts, you
- 2:22might need to explore reducing the
- 2:23collision fidelity of objects in your
- 2:25experience. So, inside of a basic
- 2:27default template base plate, if we open
- 2:30the developer console by hitting F9 on
- 2:33our keyboard, and then we open this tab
- 2:35right here and go to memory, and then we
- 2:37go and shift this or organize this by
- 2:40high memory usage by clicking this. And
- 2:43then we're going to skip over this core
- 2:44memory area because we don't care about
- 2:46any of this internal stuff. We just want
- 2:48to go down until we find the place
- 2:51memory category right here. And the one
- 2:53we're going to be specifically looking
- 2:55for is physics parts. This one right
- 2:57here. Because this is a basic template
- 2:59or a base plate. There are no meshes, no
- 3:02parts in this place. So the physics part
- 3:04memory is at 1 megabyte, which is just
- 3:07probably for my player's character. And
- 3:09that's it. Once you start building your
- 3:11map and you add more unions and you add
- 3:13in meshes and all that stuff, this value
- 3:16will slowly start to increase and you
- 3:18can fight back against how much memory
- 3:20usage goes under this category by
- 3:22reducing the collision fidelity of
- 3:24meshes and unions in your game. For
- 3:26example, here in this game that I'm in,
- 3:29it's a project that I've been working
- 3:30on. You can see that the physics parts
- 3:33memory usage is at about 420 mgby. The
- 3:36higher and higher this value gets, let's
- 3:38say you get close to a th00and megabytes
- 3:40or you go over a thousand megabytes, you
- 3:42might want to start worrying about how
- 3:44much memory usage is being used for
- 3:46collisions in your game. And a way to
- 3:49improve the value here is by going
- 3:52around to all the meshes and unions in
- 3:55your game and checking their collision
- 3:56fidelity. There's an easy way to do
- 3:58this, a really easy way to do this. If
- 4:00you go to this little thingy in the top
- 4:02right corner, this little set settings
- 4:04cog, and you open that up, you should be
- 4:06able to find somewhere in here called
- 4:08collision fidelity under physics
- 4:10simulation. So, if you enable that,
- 4:12you're going to see that your map turns
- 4:13a whole bunch of crazy colors if you
- 4:15have a bunch of meshes. Otherwise, if
- 4:16you have regular parts, they won't
- 4:18change. But this specifically colors all
- 4:20of the unions and meshes in your game.
- 4:23Now, the more colors you see means the
- 4:26higher resolution this collision is on.
- 4:29for example, this door truck. So, if we
- 4:32go down to collision, there's going to
- 4:34be a property here called collision
- 4:35fidelity. Right now, it's set to default
- 4:37for this truck door. And default along
- 4:40with precise convex decomposition. These
- 4:44ones take up the most memory usage. The
- 4:46ones that take up the least memory usage
- 4:48is going to be box and hole. And as the
- 4:50name suggests for box, it literally just
- 4:52creates a bounding box uh collision
- 4:55fidelity around the object. It's
- 4:57literally just a box. and then whole
- 4:58kind of you can imagine it as like a a
- 5:00saran wrap or a shrink wrap shrinking
- 5:03around the object. These two options are
- 5:05what you want to try and aim for most of
- 5:07the time because they use the least
- 5:08amount of memory. And if you take a look
- 5:10at my map, you can see I have a lot of
- 5:12these uh box collisions on, for example,
- 5:16these are light poles. Now, because
- 5:17these light poles actually have
- 5:19collisions disabled on them, I disabled
- 5:21the can collide property, there's no
- 5:23reason for me to be storing a bunch of
- 5:25information about the collision of the
- 5:27light pole when nothing can collide with
- 5:29it. So, I just set the collision
- 5:31fidelity to box. Uh, I believe it's
- 5:34locked right now. So, let me see if I
- 5:35can actually find it or maybe pick a
- 5:37different object. For example, right
- 5:39here, just by looking at this box, you
- 5:41probably would wouldn't be able to
- 5:42figure out what this is. But if we
- 5:43disable collision fidelity, you see it's
- 5:46a fire hydrant. Now, right now, fire
- 5:48hydrants have all their collisions
- 5:50disabled. And because they have all
- 5:51their collisions disabled, it doesn't
- 5:53really make any sense for me to store a
- 5:55whole bunch of information about the
- 5:57collision fidelity of the fire hydrant.
- 5:59Like, if I set it to default, you can
- 6:01see there's a lot of information being
- 6:02stored on here on the collision of the
- 6:04fire hydrant, which I don't need cuz you
- 6:06can't collide with it. So, I set the
- 6:08collision fidelity to box, which takes
- 6:10up the least amount of memory usage. And
- 6:12when you take a look around my map, you
- 6:14can see I've made a lot of these
- 6:15optimizations all over the place. You
- 6:17can see tons of boxes all over the
- 6:19place. You can also see these shapes in
- 6:22the distance here, these meshes or these
- 6:24mountains. And these have their
- 6:25collision fidelity set to whole. And you
- 6:27can kind of see how it's like a shrink
- 6:29wrap effect over the object. This one
- 6:31uses the second least amount of memory
- 6:34usage. The box one's the best, but if
- 6:36you can't use the box one because it
- 6:38just doesn't work for the shapes you
- 6:39want. Let me see if there's a good
- 6:41example I could find. Like for example,
- 6:43if you take a look at these buttons
- 6:44here, these buttons are kind of semi-
- 6:46cylindrical, but I I don't really want
- 6:48to have them set to box. Well, actually,
- 6:51they have their collision set to
- 6:52nothing, so it probably makes sense to
- 6:54set them to box. Let me see if I can
- 6:55find a better example. I believe there's
- 6:56a trash can in here somewhere. Here it
- 6:58is. Okay, we have a trash can that
- 7:00players can collide with. Now, because
- 7:02the trash can is cylindrical, right, it
- 7:05doesn't make sense for it to have a box
- 7:07collision group. Because if I were to
- 7:08set it to box, you got these corners
- 7:11sticking out here and players would
- 7:13collide with these corners. And that
- 7:15would be kind of weird to have a uh
- 7:18trash can like this with that kind of
- 7:20boxy collision. So that's why you have
- 7:22the hole option for collision fidelity.
- 7:26It's pretty good for cylindrical
- 7:28objects, spheres, stuff like that. You
- 7:30just set it to that and it does a pretty
- 7:32good job of shrink wrapping around it
- 7:33and using very little memory. Let me see
- 7:35if I can find any other example. For
- 7:36example, here's a triangle. Uh, this is
- 7:38just some kind of prop. Technically, I
- 7:40could make this have no collision at all
- 7:43and then set the collision fidelity to
- 7:44box. But for some reason, let's say you
- 7:46have some triangle-shaped thing in your
- 7:48game that you want players to be able to
- 7:49collide with. If we take a look at this
- 7:51thing, that's what that is. If we were
- 7:53to set the collision fidelity to
- 7:55default, you're going to see that's a
- 7:56lot of information being stored for a
- 7:58triangle. And if we were to set to box,
- 8:01eh, that's maybe not the collision that
- 8:04we want on the triangle. So, hole is the
- 8:08best option for this one. The explorer
- 8:10makes it really easy to search for any
- 8:12objects in your game that have different
- 8:14collision fidelities cuz all you need to
- 8:16do is search for the property collision
- 8:18fidelity equal to and then it'll pop up
- 8:21with all the options. So, maybe you want
- 8:23to see if there's anything in your game
- 8:24that has the precise convex
- 8:26decomposition. And in my game, there's
- 8:28nothing, which is great. If you have a
- 8:30bunch of meshes or unions in your game
- 8:33that has this property enabled, see if
- 8:35you can change the collision fidelity to
- 8:37something a bit simpler. And if the
- 8:40object is super complex and you need
- 8:42complex collision, then you probably
- 8:46would rather have the object be
- 8:49completely non-colidable, set the
- 8:51collision fidelity to box, and then
- 8:53manually construct your own collision
- 8:55fidelity just using parts. that would be
- 8:57a lot better for memory usage and
- 8:59performance instead of using the the
- 9:02precise option. Another memory
- 9:04optimization that we can take a look at
- 9:05is this category for sounds. If you
- 9:08notice that the value here in megabytes
- 9:11is extremely high for sounds. Let's say
- 9:12you have like 500 megabytes of memory
- 9:14being used for sounds for some reason,
- 9:16uh that means you have way too many
- 9:18sounds in your game that are loaded and
- 9:20just sitting around and doing nothing.
- 9:22Typically, this happens if you have a
- 9:24whole bunch of stuff in the sound
- 9:25service. So, let's say you store all of
- 9:27your game sounds inside of the sound
- 9:29service and different folders and stuff.
- 9:30You have all the music in your game, all
- 9:32the ambiance, all the sound effects as
- 9:35sound objects. Well, the problem with
- 9:37doing it that way by having a whole
- 9:39bunch of sound instances in here with
- 9:42sound IDs is that all of these sound
- 9:45instances and the sounds inside of them
- 9:48need to be stored inside of memory. They
- 9:50need to be downloaded and then they need
- 9:52to be stored in memory. That's kind of a
- 9:54waste, especially most of the time
- 9:56sounds are just sitting in the sound
- 9:58service, not being played, and they only
- 9:59play at specific moments. So, what's
- 10:02better? What's better is to not store
- 10:06any sounds in the sound service, and
- 10:08instead store your sounds inside of your
- 10:10scripts. So basically just have the
- 10:13content ID or the sound ID for your
- 10:15sound and then create the sound during
- 10:18runtime. Play the sound when you need it
- 10:20and then destroy it or clean it up
- 10:22afterwards when you don't need it
- 10:24anymore. As an example, I have a whole
- 10:26bunch of utility functions that I use to
- 10:29play sounds. And I do so by just giving
- 10:32it a table of sound properties. So this
- 10:34can be like the volume, the sound ID, if
- 10:36it belongs to a sound group or whatever
- 10:38it is. And then it plays it. And then
- 10:41when the sound is done playing, it
- 10:43destroys it for me. So that way I'm only
- 10:46creating and loading the sounds into
- 10:47memory when I need them, play them when
- 10:50I need them, and then clean them up
- 10:51afterwards. You can see that inside of
- 10:54this game, my sounds memory usage is
- 10:56only at about 58 MGB because I don't
- 10:59have a bunch of sound instances stored
- 11:01in sound service. I have them stored or
- 11:04I have their sound IDs stored inside of
- 11:06scripts. I create the sounds when I need
- 11:08them and then when I don't need them
- 11:09anymore then, you know, I that's it. I
- 11:12destroy them. When you open this thing
- 11:14up too as well, you could also see all
- 11:15of the sound instances that are
- 11:17currently loaded into memory and how
- 11:19much uh memory they take up, which is
- 11:21also pretty cool. So, this can help you
- 11:22narrow down like, hey, which sounds are
- 11:25taking up a lot of memory and figure
- 11:26out, do I really need to store this
- 11:28sound inside of the sound service or
- 11:31inside of some object somewhere? and
- 11:34instead can I just create it during
- 11:35runtime and then only play it when I
- 11:37need it. Along with storing sounds in
- 11:39the sound service, you should also make
- 11:42determinations of whether you need
- 11:44something to be stored in replicated
- 11:46storage or if it can be stored inside of
- 11:48server storage. A lot of the times there
- 11:51are instances or objects that you can
- 11:54move out of replicated storage and store
- 11:55it in server storage. For example, let's
- 11:57say your game has a bunch of maps. Let's
- 11:58say you got 10 different maps on your
- 12:00game and the server loads different maps
- 12:02at different times. Realistically, a
- 12:04client doesn't need to have all 10 10 of
- 12:06those maps in a folder stored in
- 12:08replicated storage because it needs to
- 12:10reserve memory to store information
- 12:11about all the instances and everything
- 12:13in those maps. If those maps are only
- 12:15supposed to show up at a certain point
- 12:17in your game, then you should probably
- 12:18store them in server storage. By storing
- 12:20objects inside of server storage, you're
- 12:23preventing the clients from having to
- 12:25store all that information inside of
- 12:27memory along with having to download it
- 12:30when they join the game, which can also
- 12:32improve your loading times. Another way
- 12:34of improving memory usage is by cleaning
- 12:36up connections connected to player
- 12:39instances and their character models.
- 12:41So, inside of the workspace, if we go to
- 12:43the properties window, there's going to
- 12:45be a property down here called player
- 12:47character destroy behavior. You should
- 12:49probably have this set to enable. The
- 12:52default is default. And I don't know if
- 12:54the default is enabled, but you you
- 12:56probably just want to make sure this is
- 12:57set to enable because the prior behavior
- 12:59with players and their character models
- 13:01is that when a character model dies or
- 13:03the player left the game, their instance
- 13:06would only be parented to nil. But the
- 13:08destroy function would not be called on
- 13:10the character models or the player
- 13:12models. So if you had any connections to
- 13:15any events to the player or anything
- 13:18inside of the character model, stuff
- 13:19like that, those connections would sit
- 13:21around in memory. And as more and more
- 13:23players join the game, leave the game,
- 13:25spawn, and respawn, you would have a
- 13:27bunch of memory leaks and building up of
- 13:29memory in your game till eventually
- 13:31maybe your server would crash, and then
- 13:33you'd have to get a new server and start
- 13:34the process all over again. So it it's
- 13:36just a good idea to have this player
- 13:38character destroy behavior set to
- 13:40enabled. The next area we're going to be
- 13:42taking a look at improving is going to
- 13:44be this graphics texture category. If
- 13:47you have very high memory usage, let's
- 13:49say you have like 500 megabytes or more,
- 13:51a thousand megabytes, whatever it is for
- 13:53graphics texture, you really need to ask
- 13:55yourself, do I need those high
- 13:57resolution 1,024x 10,024 pixel textures
- 14:01on multiple different objects with all
- 14:02their own unique textures, or am I able
- 14:05to downsize those textures to something
- 14:08smaller like 512 x 512 pixels? And can I
- 14:12use things like texture atlases? And can
- 14:15I use things such as I believe they're
- 14:18called, what are they called? Trim
- 14:20sheets. Texture trim sheets. There's a
- 14:22bunch of little hacky things that people
- 14:23can do with textures to be able to reuse
- 14:26them across multiple objects and keep
- 14:29this memory usage down. So that way
- 14:32you're not having to have a bunch of
- 14:34client memory usage being wasted because
- 14:36you decided to upload super high
- 14:38resolution textures. In fact, if we look
- 14:40at the documentation, it says a 1,024x
- 14:431024 pixel texture consumes four times
- 14:48four times the graphics memory of a 512x
- 14:52512 texture. Images uploaded to Roblox
- 14:55are transcoded to a fixed format. So,
- 14:57there's no memory benefit to uploading
- 14:58images in a color model associated with
- 15:00fewer bytes per pixel. Similarly,
- 15:02compressing images prior to upload or
- 15:04removing the alpha channel from images
- 15:06that don't need it can decrease image
- 15:08size on disk but either doesn't improve
- 15:10or only minimally improves memory usage.
- 15:13So, as I said, if you can downsize your
- 15:16or scale down your textures, do it. If
- 15:18you can use texture atlases, do it. If
- 15:21you can use trim sheets, do it. This
- 15:23will help save memory usage in relation
- 15:25to the graphics texture category for
- 15:29your game. Another incredibly important
- 15:31feature inside of the Roblox game engine
- 15:34is going to be streaming enabled. Right
- 15:36now, I don't have it enabled in this
- 15:37game, but when you enable this, this
- 15:39allows you to stream in and out objects
- 15:42inside of the workspace, which can help
- 15:44improve load times and reduce memory
- 15:47usage. I'm not going to go into
- 15:48everything about streaming enabled. That
- 15:51can be a video on its own that will
- 15:54probably be like 20 minutes long, so I'm
- 15:56not going to go into it. This will
- 15:57probably be a future video, but I will
- 15:59leave a link in the description to the
- 16:01Roblox documentation all about streaming
- 16:04enabled. The next area we're going to be
- 16:05taking a look at improving performance
- 16:07is going to be frame rate. And what's
- 16:09the easiest way you could do that? Well,
- 16:11the easiest way you could do that is by
- 16:13reducing the number of draw calls
- 16:15happening every single frame. So, inside
- 16:17of Studio, if you opened this statistics
- 16:21menu by hitting control shift and I
- 16:23believe it is what is F2? Yes, F2.
- 16:25Controll Shift F2, you get this menu
- 16:28popping open. And what we're interested
- 16:30in is this thing down here that says
- 16:32draw counts. And the one that we're most
- 16:34interested in is going to be this scene
- 16:36draw count. So Mr. Chicken Rocket
- 16:39recommends that you should have a budget
- 16:42of 500 in scene draw counts and about
- 16:45500,000 triangles. I'm a little bit
- 16:47above that, 590,000 triangles, but I am
- 16:49below the 500 draw call count budget. If
- 16:54you have this number or this value of
- 16:56scene draw counts in the thousands,
- 16:59you've got a problem and you need to
- 17:00optimize your game to figure out how to
- 17:02make sure your draw counts don't go
- 17:04above a thousand or 500 or something
- 17:06like that. Usually, it's okay to go
- 17:08above 500, but 500 is a a good metric to
- 17:11hit, especially for mobile devices. Now,
- 17:14Roblox did introduce their occlusion
- 17:16calling feature. So when you the camera
- 17:19blocks objects from view, you're going
- 17:21to notice that the scene draw counts
- 17:23reduce because I'm not rendering a bunch
- 17:25of stuff. Like if I put my camera into a
- 17:27wall, you can see that the draw counts
- 17:30reduce. And as I, you know, look around
- 17:32at other stuff, you can see that they
- 17:33increase because I'm having to draw more
- 17:35meshes and triangles and things like
- 17:37that. But we could actually see this in
- 17:39action. Let me see if I can find it's in
- 17:42here. Yes, wireframe. Let's go into
- 17:45wireframe mode. So here we can see all
- 17:47the triangles being rendered. And if I
- 17:49look inside of a wall, you're going to
- 17:50notice that very few things are being
- 17:52rendered except for for some reason
- 17:54there's a fountain over there being
- 17:56rendered, which is kind of interesting.
- 17:58Character NPC models also don't get cold
- 18:00by the occlusion coin. And for some
- 18:02reason, grass doesn't either, which is
- 18:04pretty interesting. Another thing that
- 18:06doesn't get cold by occlusion coin are
- 18:08particle emitters and beams and I
- 18:11believe trails as well. Oh, you can see
- 18:12those trails moving and they don't seem
- 18:15to be getting cold. And the crazy thing
- 18:16about particle emitters and beams is
- 18:18that each particle emitter and each beam
- 18:21takes one draw call even if they use the
- 18:24same texture. Because typically when you
- 18:26have a mesh, for example, this light
- 18:29post here, because these light posts all
- 18:31use the same mesh IDs, they all get
- 18:34batched into a single draw call. So,
- 18:36even if I were to look at 100 of these
- 18:40light poles in a scene, it only takes
- 18:42one draw call to draw all of the light
- 18:44poles, which is pretty cool.
- 18:46Unfortunately, that doesn't happen with
- 18:47particle emitters or beams. They don't
- 18:49get cold by occlusion coin. So, that
- 18:51means you're kind of left to do it
- 18:52yourself or suffer the consequences. For
- 18:55example, here are some particles and
- 18:57here are some beams being rendered on
- 19:00screen. Now, I have a system in place,
- 19:02my client fidelity service, which I made
- 19:04a video on. you could check it out where
- 19:07once the camera or the character gets
- 19:09far enough away from these beams or
- 19:11particle emitters, I call them or I
- 19:14disable them so they're not being
- 19:15rendered because realistically if let's
- 19:18say I'm all the way over here, let me
- 19:20teleport all the way over here. There's
- 19:22no reason why I should be rendering
- 19:23that. And actually, you could notice
- 19:24that they disappeared. They're not being
- 19:26rendered anymore because I'm too far
- 19:27away. I'm too far away to notice or care
- 19:30about those beams or particle emitters.
- 19:31So, why do I need to render them? So, I
- 19:33disable them. I also do the same thing
- 19:35for lights in my game as well. You can
- 19:37see when I walk away, you can see those
- 19:40lights getting disabled and things like
- 19:42that. And if I open the settings of my
- 19:44game and I disable high graphics
- 19:46fidelity, I also disable shadows and
- 19:48global shadows, which also helps with
- 19:51the shadow draw count. Though the shadow
- 19:53draw count is still pretty high because
- 19:56if I go to my lighting service, I am in
- 19:58the future lighting technology. If you
- 20:00set this to shadow map and you disable
- 20:03global shadows, you're going to have
- 20:05zero draw counts, which is really good
- 20:07for mobile players because they don't
- 20:09have to waste time rendering shadows.
- 20:10And do mobile players really care about
- 20:12shadows? I don't know. I think mobile
- 20:14players would actually rather care about
- 20:16the game being playable instead of it
- 20:19running two frames per second. That's
- 20:21just my opinion. As you can see over
- 20:23there, I have more particle emitters,
- 20:25beams, and even some decals over there.
- 20:27Slightly transparent decals. And by the
- 20:30way, if you have a whole bunch of
- 20:32transparent textures overlapping each
- 20:34other, that can cause something called
- 20:36high transparency overdraw, which is
- 20:37also not great for performance. But
- 20:39because I'm on low fidelity mode and I
- 20:42go and I walk away from these decals,
- 20:45eventually boom, they all disappear.
- 20:47They get cold. They get disabled. And
- 20:49that helps reduce the amount of draw
- 20:51counts happening. and also helps disable
- 20:53the issue or mitigate the issue with the
- 20:56high transparency overdraw with those
- 20:59transparent decals because they aren't
- 21:01being rendered. Now, usually the culprit
- 21:03of a very high draw count is either one
- 21:06of two things that I've seen. One is
- 21:08that you have way too many unique meshes
- 21:10in your game. And for some reason, you
- 21:13might have the same meshes but with
- 21:16different mesh IDs which are being
- 21:19batched into separate draw calls which
- 21:21are increasing your draw call count. Or
- 21:23you have a bunch of particle meters in
- 21:24your game all over the map that are
- 21:26enabled that aren't being called and
- 21:28they're just being rendered. It's
- 21:29probably one of those two things. As an
- 21:31example, if you take a look at this
- 21:33foliage, I have some dead trees and I
- 21:36have some trees with leaves. Now, these
- 21:39trees were actually imported as separate
- 21:41meshes. So, originally, this mesh right
- 21:44here, despite it being the exact same
- 21:46mesh as this one, they're both literally
- 21:49the exact same, they both had different
- 21:51mesh IDs. Their mesh IDs were different.
- 21:53And because of that, that means instead
- 21:55of these being batched into the same
- 21:57draw call, these are being batched into
- 21:59two separate draw calls, which isn't
- 22:01great. So, I went in and fixed that by
- 22:04replacing all of the I think it was the
- 22:08dead trees. I replaced all the dead
- 22:09trees to have the same mesh ID as the
- 22:12alive trees. And that fixed that draw
- 22:14call issue. Another example I could give
- 22:16you are the shipping containers over
- 22:18here. So, if you take a look at these
- 22:20shipping containers, not only do we have
- 22:22the body of the shipping container, but
- 22:24we have four doors. And you know what's
- 22:27interesting about these four doors?
- 22:29Well, they're all the same mesh except
- 22:31for the fact that they have like flipped
- 22:33hinges. So, technically, I should only
- 22:36need two draw calls. One for the left
- 22:38door and one for the right door. But
- 22:40because these were imported with all the
- 22:42doors on them, each door had its own
- 22:44unique mesh ID. So, I needed to fix that
- 22:47as well by just reusing the same doors
- 22:50over here and then moving them over here
- 22:52and flipping them. And that reduced my
- 22:55draw calls for the shipping containers
- 22:57by 50%. Those are things that you just
- 22:59need to watch out for when you're
- 23:01importing meshes. Let me give you
- 23:03another example. There should be a truck
- 23:05around here somewhere. Let me find a
- 23:07truck. This guy should have a truck. Why
- 23:09is it Where where did the truck go?
- 23:11There were trucks here and now they're
- 23:12gone, which is actually quite
- 23:14interesting. That That's kind of weird.
- 23:16Can I find a different truck somewhere?
- 23:17I I'm going to have to fix that. Okay,
- 23:19here. Look, there's a truck over here.
- 23:21If we look at these wheels on the truck,
- 23:23right, they're literally the exact same
- 23:25mesh. They're just flipped around. And
- 23:28then with the rear tires, these these
- 23:30are both the exact same meshes. But
- 23:32because this truck was imported together
- 23:34with all the wheels on them, each wheel
- 23:37or each tire has its own unique mesh ID.
- 23:40So, if we look at this one, so the mesh
- 23:42ID ends in 714. And if we look at this
- 23:45one, the mesh ID ends in 505. So, look
- 23:49at that. This is something I need to fix
- 23:50because these these tires are the exact
- 23:53same, but they're being batched into two
- 23:55separate draw calls. Not great. The more
- 23:57and more meshes you add into the game
- 23:59that have these kind of issues, the more
- 24:02and more your draw calls will pile up,
- 24:04and eventually you're going to have to
- 24:06fix it. Another area of frame rate
- 24:08optimization you might want to take a
- 24:09peek at for your meshes is going to be a
- 24:12property called render fidelity. Now
- 24:15typically you'll want to have this
- 24:17fidelity set to automatic or you can set
- 24:20it to performance on certain meshes.
- 24:22Though setting it to performance might
- 24:24alter the visual look of meshes in some
- 24:27cases. So you might need to play around
- 24:28with these properties and see if
- 24:30performance would work. Otherwise
- 24:32automatic should work for most cases.
- 24:34Now, if you have a bunch of meshes in
- 24:36your game that have their render
- 24:37fidelity set to precise for some reason,
- 24:40you probably want to set it to
- 24:41automatic. Cuz if we go and we search in
- 24:44my game, render fidelity equals 2, and
- 24:47we check to see if there's any meshes
- 24:48with precise. Actually, we do have some
- 24:50meshes with precise, which I'll probably
- 24:51want to change and disable. Otherwise,
- 24:54the default meshes in here that are set
- 24:56to precise are probably character
- 24:58models. I think it's only character
- 25:00models that have the render fidelity set
- 25:02to
- 25:04precise. But as you can see, there's
- 25:06some meshes here in regards to this card
- 25:08scanner. So, I want to make sure all of
- 25:10these meshes are set to automatic
- 25:14instead of precise because if I'm super
- 25:16far away, there's no reason for me to be
- 25:18precisely rendering the mesh. If we look
- 25:20in the Roblox documentation under the
- 25:23meshes section, there's a level of
- 25:25detail category and it says you can
- 25:27dynamically control a mesh's level of
- 25:29detail using its enum render fidelity
- 25:32property. The default value is
- 25:34automatic, meaning the mesh's details
- 25:37based on its distance from the camera as
- 25:39outlined in the following table. So,
- 25:41this is an LOD system for meshes. If
- 25:45you're less than 250 studs away from the
- 25:47camera, then it renders it in the
- 25:48highest fidelity. If you're about 250 to
- 25:51500 studs away, then it renders it in
- 25:54medium fidelity. And when you're greater
- 25:55than 500 studs away, the mesh is
- 25:58rendered in the lowest fidelity. And you
- 26:00can kind of see how it gets decomposed
- 26:03over time as the rendering becomes less
- 26:06precise. Probably the amount of
- 26:08triangles gets reduced automatically
- 26:10through some internal engine operation.
- 26:13But just having all of your meshes set
- 26:15to automatic should be good for most
- 26:17cases. If there's a way for you to set
- 26:19the level of detail to or the rendering
- 26:21fidelity to performance, push as much
- 26:24performance as possible, then you can
- 26:26totally do that. However, it says this
- 26:28means that the performance will always
- 26:31be excellent, but mesh visuals may be
- 26:34affected negatively. So, you need to
- 26:36kind of experiment with this. Another
- 26:38area you can make with performance
- 26:40improvements are humanoids. So, if I
- 26:43take a look at one of these humanoids,
- 26:46this is a static NPC. It doesn't move
- 26:48around. He can't move around. He just
- 26:49stands there and you talk to the NPC.
- 26:52Now, because of this, it doesn't make
- 26:54sense for the humanoid to be calculating
- 26:56all of its different states because
- 26:59humanoids have a bunch of different
- 27:00states like running, sitting, falling
- 27:03down, getting up, jumping, all that kind
- 27:05of stuff. Why does the humanoid need to
- 27:07evaluate for all those different states
- 27:09when it's a static NPC? So, what's the
- 27:11smart thing to do with your static NPCs
- 27:13that have humanoids in them? Go over to
- 27:15the humanoid properties and disable
- 27:18evaluate state machine. By disabling the
- 27:21state machine, you are now no longer
- 27:23wasting CPU time calculating all the
- 27:26stuff for the humanoid. Now, if you do
- 27:29need to have your humanoids move around,
- 27:31but let's say they only walk around,
- 27:33they don't jump, they don't climb, they
- 27:35can't fall over, then you can also go
- 27:37through and disable certain humanoid
- 27:39states. So that way you're not
- 27:41calculating those either. By going
- 27:43through and disabling certain humanoid
- 27:45states that you're never going to use,
- 27:46that will also help improve performance
- 27:48with your humanoids. Another area of
- 27:51performance improvement is when you have
- 27:53a lot of moving objects in your game
- 27:55that you're updating every single frame.
- 27:57So if you look, I have all of these
- 27:59spinners spinning in this kind of obby
- 28:02area. I've got those spinners. I got
- 28:03these shark spinners. They're all
- 28:05spinning around. Now, these are actually
- 28:07being uh Cframe updated on the client
- 28:10side for every single client. And then
- 28:12they're all synced together by using a
- 28:14timestamp of when the spinner was
- 28:16initialized on the server side. So that
- 28:18way all the clients have their spinners
- 28:20synced up so they look the same across
- 28:22clients, but all of these spinners are
- 28:25having their C frames updated every
- 28:27single frame. Now, if you were to do
- 28:29that individually and let's say you had
- 28:31hundreds or thousands of these parts in
- 28:33your game, that can take up a lot of CPU
- 28:36time. So, a way you can fix that is by
- 28:38using bulk C frame updates using a
- 28:42method in the workspace. If I go down to
- 28:44the command bar here, there's a method
- 28:46in here called bulk move to moves a
- 28:49table of base parts to a table of C
- 28:51frames. And by using this method, it
- 28:53allows you to update the C frame
- 28:55properties of a crap ton of parts really
- 28:58quickly. So inside of my code, every
- 29:00single heartbeat, I'm running this obby
- 29:03spinner update, and I go through all the
- 29:05spinners in my game, and I calculate the
- 29:08correct C frame. I get this new C frame,
- 29:10and then I simply insert it into a parts
- 29:13table and a C frames table. And then
- 29:16once I've gone through all the spinners
- 29:18in my game, then I do a single bulk move
- 29:20to update on all the parts with their
- 29:23associated C frames. And that's a lot
- 29:25faster if I were to instead of doing
- 29:28bulk move to I were to go in here. I
- 29:30know I'm in testing mode. I can't make
- 29:32any changes. If I were to do spinner C
- 29:34frame is equal to new C frame and I just
- 29:37did that for every single spinner. This
- 29:38will actually take longer than instead
- 29:41of just doing bulk move to. If we open
- 29:43up the microrofrofiler and then I were
- 29:45to pause the microprofiler and then let
- 29:47me select some random frame in here like
- 29:50this one. And then let's find heartbeat.
- 29:52There's heartbeat. There's also another
- 29:53heartbeat down here. If we go down into
- 29:55heartbeat, there's my obby spinner
- 29:57update right there. And it's taking
- 30:00approximately 36 milliseconds to update
- 30:04all of the spinners inside of my obby.
- 30:07Not bad. Now, I've got a new idea. Let's
- 30:10go ahead and comment this code out.
- 30:12Let's comment this code out. Let's
- 30:15comment this code out. And instead of
- 30:17doing the bulk update, let's just do
- 30:19spinner C frame is equal to new C frame.
- 30:23And let's see how much of a difference
- 30:25that makes to the frame time. So, we'll
- 30:27go ahead and pause the micropriiler.
- 30:29We'll select the random frame in here
- 30:31like this one. And then there we go.
- 30:33Here's our obby spinner update. And it
- 30:36is now taking49
- 30:39milliseconds instead of the.3 something
- 30:42milliseconds. You might be thinking that
- 30:44wasn't a lot. And that's right because
- 30:46there's not that many spinners being
- 30:48updated in here. But when you have a
- 30:51crap ton of parts that you need to
- 30:52update simultaneously, let's say on a
- 30:54per frame basis or maybe even just once,
- 30:57using bulk move 2 over individually
- 31:00setting all the C frames can be a
- 31:02gamecher. And let's actually see if we
- 31:04can improve the performance further with
- 31:06bulk move 2 by specifying a specific
- 31:09event mode. So let me uncomment that.
- 31:13Uncomment that. Uncomment this. And
- 31:16there's a third parameter we could pass
- 31:18here called bulk move mode. So we could
- 31:20do enum bulk move mode. And instead of
- 31:23firing all events, which I believe is
- 31:25the default for this function, let's
- 31:26just fire C frame changed. And this
- 31:28should also hopefully improve bulk move
- 31:31to even further. Let's go ahead and
- 31:33pause. Let's pick a random frame. And
- 31:36then here is our obby spinner update.
- 31:37How long did that take? Look at that. It
- 31:39took even less time by just firing the C
- 31:42frame changed event. 255 milliseconds.
- 31:46So we went from 0.49 down to.255
- 31:51just by using bulk move to. And when you
- 31:53have a lot of objects in your game, that
- 31:57about 50% performance improvement can
- 31:59really add up. Now, there are a couple
- 32:02more properties inside of the workspace
- 32:04that you'll likely benefit from
- 32:05enabling. So, if we go back into the
- 32:07workspace, there's going to be a
- 32:09property in here called client animator
- 32:12throttling. Make sure you have that
- 32:14enabled. Basically, what this property
- 32:16does is that the farther away an
- 32:19animator instance is away from the
- 32:21player's character or camera or
- 32:23something like that, it'll update that
- 32:25animation at a slower rate. So, it'll
- 32:28only do like intermittent updates to the
- 32:30animation when it's far away cuz you
- 32:31can't really see it. And any animators
- 32:33that are nearby to your camera or your
- 32:36character will be updated at their
- 32:37normal rate. And this can also just help
- 32:40boost performance a little bit with
- 32:41animators. Another property is going to
- 32:43be called physics stepping method. And
- 32:47this will be set to default by default.
- 32:49If you set it to the adaptive mode, this
- 32:51allows the engine to make some decisions
- 32:55on how it wants to do some physics
- 32:57calculations and it reduces certain
- 32:59amount of physics calculations.
- 33:01Basically, just improves performance in
- 33:03regards to physics. So, you probably
- 33:05want to have this set to adaptive. Last
- 33:07area of optimization we're going to be
- 33:09focusing on is networking. And
- 33:12specifically, you're going to be taking
- 33:13a look at this incoming or overall
- 33:16incoming network data in kilobytes per
- 33:18second. Now, a good baseline, as Mr.
- 33:21Chicken Rocket noted, is that you want
- 33:23to have this value below 50 kilobytes
- 33:26per second on a consistent basis. If you
- 33:28see that your overall incoming kilobytes
- 33:30per second is above 50 kilobytes per
- 33:33second, that likely means you have way
- 33:35too many assemblies in your game that
- 33:37the server is having to replicate. Now,
- 33:39an assembly is basically an unanchored
- 33:42conglomeration of physics. So, things
- 33:44attached together with physics
- 33:46constraints like welds that are
- 33:48unanchored with some unanchor point or
- 33:50you have character models connected
- 33:52together with motor 60s or you have
- 33:54different physics constraints like
- 33:56hinges and things like that. When you
- 33:58have tons of those things in your game,
- 33:59the high incoming network usage with
- 34:02Roblox physics kind of makes it hard to
- 34:04work with, especially if you need
- 34:05servers valided physics, which hopefully
- 34:08maybe Roblox's servers aurora thing
- 34:11they're working on in the future might
- 34:12fix this problem, but who knows? But you
- 34:14need to make decisions on how many
- 34:16things you want in your game to be
- 34:17unanchored, how many assemblies you have
- 34:19in your game that are moving around, and
- 34:20any optimizations you can make around
- 34:22those assemblies. Like for example, I
- 34:24could have totally opted to make all of
- 34:26these spinners use physics constraints.
- 34:29I could have had like, I don't know,
- 34:31hinges or something in here and had like
- 34:33forces applied to have these things
- 34:35spinning. That would have tanked or not
- 34:37tanked, that would have just made my
- 34:39overall incoming kilobytes per second
- 34:41like crazy with all these spinners. It
- 34:43probably be over 100 kilobytes per
- 34:44second or something like that. Not good.
- 34:47So, when you can figure out ways to not
- 34:49use physics, don't use physics. When it
- 34:52comes to players character models,
- 34:54that's kind of inevitable because
- 34:55players need to run around. And when you
- 34:56have NPCs in your game or like AI that
- 35:00the server controls that get moved
- 35:02around, that's kind of another point of
- 35:04friction where you got to figure out,
- 35:07okay, maybe I need to make my own
- 35:09servers replication system to control
- 35:13how much data I send over for stuff.
- 35:15It's kind of a complex solution.
- 35:17Basically, you just want to make sure
- 35:18that you reduce the amount of assemblies
- 35:20in your game when possible, like with
- 35:21the spinner example. Reduce the amount
- 35:23of unanchored parts in your game. And
- 35:25then, if you're able, you could also use
- 35:28IK control instances or inverse
- 35:30kinematics to try and offset the issue
- 35:34with overall incoming network data. And
- 35:36that'll probably be a future video cuz
- 35:38there's actually something really
- 35:39powerful with inverse kinematics that
- 35:41allows you to animate like objects and
- 35:45make them look like they are using
- 35:46physics or animation or whatever, but it
- 35:48has zero impact on the overall incoming
- 35:51data, which is really cool. I'll make a
- 35:53future video on it. I promise.
- 35:54Otherwise, another point that you have
- 35:56with controlling how much overall
- 35:58incoming data there is is your remote
- 36:00events. So let's say you have something
- 36:02very frequently happening in your game
- 36:04where you need to constantly send out
- 36:05data to players. Maybe you can serialize
- 36:08that with buffers. I have an entire
- 36:10video about buffers and one of the main
- 36:12utilizations of buffers is for your own
- 36:14replication systems for compressing down
- 36:16data. In fact, there's this really
- 36:18awesome dev forum post that talks about
- 36:21how these developers reduced bandwidth
- 36:24usage by 60 times in their game. Cuz
- 36:27originally their overall bandwidth usage
- 36:29on the incoming was 300 to 350 kilobytes
- 36:33per second. Not good. And the reason why
- 36:36is because they had a bunch of parts on
- 36:38the server that were moving around and
- 36:40humanoids. And because there's so many
- 36:43of those parts moving around as
- 36:45assemblies that just takes up so much
- 36:48overall bandwidth usage, which is why
- 36:50they had over 300 kilobytes per second.
- 36:52And eventually they came to a point
- 36:54where they decided, hey, we can't do any
- 36:56of this stuff on the server. The server
- 36:57just needs to store positional stuff.
- 36:59And then the clients have to control the
- 37:01visualization of things and linearly
- 37:03interpolate the NPCs and stuff like
- 37:05that. It's a lot of work to fix this
- 37:08kind of replication or bandwidth usage.
- 37:10And it's unfortunately something I'm
- 37:12going to have to do here pretty soon
- 37:14with a system I need to make. kind of
- 37:16sucks that you have to do it this way,
- 37:18but Roblox does provide the tools with
- 37:20buffers and things like that to be able
- 37:22to actually code a system like this and
- 37:24bring your overall bandwidth usage down
- 37:26from 310 to 4.8 to 5.2, which is crazy.
- 37:30Otherwise, a couple other simple ways to
- 37:32avoid having very high overall incoming
- 37:35data is one, don't tween on the server.
- 37:38Never tween on the server. Don't do
- 37:40that. Number two, don't play animations
- 37:42on the server side. If you need to play
- 37:44animations, have the clients play the
- 37:46animations. Tell the clients through
- 37:48remote event or something, hey, I want
- 37:49you to play an animation on this guy and
- 37:51have the clients do it. Because
- 37:52otherwise, if you play twins on the
- 37:54server or you play animations on the
- 37:56server, all of that data has to get
- 37:58replicated to the clients. And that will
- 38:00boost up your overall incoming kilobytes
- 38:02per second. Along with that, making a
- 38:04bunch of C frame updates on the server
- 38:06also impacts the overall incoming data
- 38:09as well. Because for example, with these
- 38:10spinners I've got here, I could have
- 38:12totally simulated the C frame updates
- 38:14for these spinners on the server side.
- 38:17And you know what that would have meant
- 38:18for my overall incoming data? It would
- 38:20have meant disaster because that would
- 38:22be so many C frame updates needing to be
- 38:25sent to all the players in the game when
- 38:27I could just have the players simulate
- 38:30the spinners on the client side instead.
- 38:32Now, there's a whole bunch more stuff I
- 38:34could talk about in regards to
- 38:36performance, but that would make this
- 38:37video way too stinking long. So,
- 38:40instead, I'm going to leave a whole
- 38:41bunch of resources in the description,
- 38:43such as these articles, dev forum posts,
- 38:45dev forum post to Mr. Chicken Rockets
- 38:47post, stuff like that. So, you can go
- 38:48ahead and take a look and read through
- 38:50them and see if there's anything that
- 38:52can be of use to you to improve the
- 38:54performance issues in your games.
- 38:57Otherwise, thank you for watching and I
- 38:59will see you in the next
About this transcript
This page contains the full transcript of Your Roblox Game LAGS (and how to fix it) by Crusherfire, generated from the public captions YouTube serves with the video. The transcript has 7,501 words across 1,076 segments, with the original timestamps preserved so you can click any line to jump to that moment in the embedded player.
What you can do with it
Use the transcript to take notes, quote the speaker, build a study guide, generate a summary with ChatGPT or Claude via the YouTube Summary tool, or export it as a timed subtitle file with YouTube to SRT. You can also re-open it in the transcriber to choose a caption track available on the video.
Free YouTube transcript tool
YouTube2Text is a free YouTube transcript generator — no signup, no daily limit. Paste any YouTube link and get the full transcript instantly, with timestamps, click-to-jump, caption language selection, AI prompts for ChatGPT, Claude, and Gemini, and exports to TXT, SRT, VTT, or Markdown.