How do you optimize and organize your code?

The base of my new game is just about complete. I am working on this game solo, and this will be my very first project where I am doing the coding. I want to ask all of you. How do you organize your scripts? How do you optimize the code?

I want to make sure things are done right the first time, so I don’t have to go back and rewrite/relocate code.
What do you do?
Do you block things out and come back later to clean up? Do you just know how to construct your game properly and efficiently with experience?

What are your thoughts/tips?

11 Likes

My strategy is to actually create what I’m making first. Once I have a rough prototype done, I go back and organize the code to favor readability and organization. I also will look for ways to optimize the code if it’s needed, such as fixing memory leaks. It’s important to keep in mind that you are most likely going tot touch your code again, so you should probably know where everything is, what it does, etc. with ease. I typically try to do with a modular design as it’s easy to follow and edit.

5 Likes

What I do on my Roblox projects applies to my practices at work (a major part of my job is back-end Java programming). Here’s a very simplified paragraph:
I pretty much start with a lengthy design phase in order to learn the goal, requirements, and any limitations for the project. This specification would then allow me to choose the best way to code things in order to achieve not only a working result, but one that is fast, safe, and reliable. Once that is done, of course, you can always optimize things. No code is perfect!

In terms of Roblox game development, I recommend that you specify exactly how you want to structure your Scripts, to avoid confusion later on. I recommend some form of documentation for this stuff :slight_smile:

14 Likes

Usually I start out with a plan on how I want everything to look so that it’ll be easier to go back and change stuff. It’s usually very common for me to create functions in modulescripts for doing very simple trivial tasks (e.g accessing a table) so that if I ever want to go back and change something, I can just change one instance of it and then I don’t have to edit a bajillion different scripts. I usually do this with datastores, accessing them all through one modulescript (which means that I can even integrate my own cache very easily or log datastore requests, HTTP requests, etc)

Most of my initial work on scripting is rough and usually just so that I can get a quick prototype/proof of concept out. When everything works I go back and tidy it all up, moving stuff around, changing how things are done, et cetera. It’s much easier for me to change features, add features or even rework an entire feature when I have a pre-existing framework of stuff that works anyway that I can base it off of. And document everything!

3 Likes

As a learner I often will rework some of my older work.

I’ve learnt that documention is important for me so I often create flowcharts and other simular diagrams to show later what my code will do.

I’ll often try and create something that’ll just work and then try to find ways of improving it until I feel it’s not worth it.

Although you should find your own way

1 Like

Before I continue, let me just say one thing: There is no right way to do anything. What works for someone else may not work for you.

Honestly, I don’t do a lot of planning in terms of the structure. Usually I just arbitrarily come up with a structure that is within or close to my comfort zone and just go with that. When my writing my first bits of code is when I come up with the core conventions; things such as variable naming, script and object organisation, etc. It’s kind of intuitive.

But of course it has its problems. For example, sometimes I do need to change the way things work. Often I am designing the game as I script it, so requirements for things change also. However, doing these changes usually doesn’t take too long if you have stuff that is already working, and your biggest enemies are bugs that may take a while to solve. Either way, this can often interrupt the game’s schedule.

But that also has its positives. It usually means I’m flexible as a developer and can redesign my game when I find an issue with its design. I’m not locked into a specific schedule or structure and can adapt. That’s what I like doing, and admittedly I haven’t tried forcing myself into a rigid, well-planned project before.

I don’t actually document my code that much; usually everything is organised and named in such a way that documentation isn’t required for the most part. The only things I need to document are the rare, weird bits of code I need to run to fix some logic-related bug or whatever. This code feel dense and overwhel lack of documentation (lack of whitespace, really) often makes the ‘bigger picture’ of theming but that feeling is quickly subdued when you actually read the code.

Fairly extreme example of my code
local CreateButton = function(ItemId, ItemData, ItemAmount, Order, Container)
	local ItemType = ItemData.Type
	
	local ItemButton = ItemTemplate:Clone() do
		ItemButton.LayoutOrder = Order
		ItemButton.Name = ItemId.."Button"
		ItemButton.Parent = Container
		
		local StackLabel = ItemButton.StackLabel
		StackLabel.Visible = ItemAmount > 1
		StackLabel.Text = "x" ..ItemAmount
			
		local ItemViewport do
			local ViewportItem = ItemsModule:Get(ItemId)
			ItemViewport = ViewportManager.new(ItemButton.ItemViewport)
			ItemViewport:SetItem(ViewportItem, ItemData.ViewportOffset)
		end
	end
	
	if IsEquipped(ItemId, ItemType) then
		SetItemEquippedVisual(ItemButton, true)
	end
	
	HoverBindable:Fire("Bind", ItemButton, function()
		return {
			Text = ItemData.Name.. ((IsEquipped(ItemId, ItemType) and " (EQUIPPED)") or "")
		}
	end)
		
	local Debounce = false
	
	ItemButton.MouseButton1Click:Connect(function()
		if Debounce then return end
		Debounce = true
		
		if CanEquip(ItemId, ItemType) then
			EquipItem(ItemButton, ItemId, ItemType)
		end
		
		Debounce = false
	end)
	
	return ItemButton
end

In the example, you can see that I’m somewhat fond of the do block. They’re pretty useful for organising different parts of your code that require a variable that isn’t needed elsewhere. In this example, StackLabel and ItemViewport don’t get used outside of the ItemButton block, so I stuck em in there. This isn’t actually correct organisation according to my internal book but it still shows off what can be done. The ItemViewport block is a good example of correct organisation, though, due to it using a variable used nowhere else.

In terms of organisation of your objects, I did things such as

  • ServerScriptService
    • Players (script)
      • Data (module)
      • DataStore (module)
      • Leaderstats (module)

and

  • ReplicatedStorage
    • Assets (folder)
      • Weapons (folder)
        • Sword (tool)
    • Modules (folder)
      • Items (module)

Anyway, hopefully this all helped you figure out where you want to go :wink:

23 Likes

As EmeraldSlash said, there is no correct way of doing something. Some like to do it this way, some don’t. It really just comes back to you.

For me, I like my scripts to be really organized so it can be more readable later on when debugging, improving, or rewriting the script. I include comments literally everywhere, especially if I’m working on a team project.
There’s a certain order that I always follow no matter what I’m doing, unless it’s something useless I’m scripting (won’t be used and more of a test).

Variables (in each section, variables are listed according to when they’re used):

  • Services
  • ModuleScripts
  • High-Priority Variables (variables that are a higher priority and will be used in the following sections)
  • High-Priority Functions (same concept as the HP variables (e.g: retrieving the player’s character))
  • Main Variables (such as player, mouse, character, object references, etc.)
  • Settings (variables that contain values that can be changed later on instead of going through the entire script)
  • Other Variables (variables that the script will change its values, such as a Running bool)
  • Animations
  • Functions (both local functions and remote functions)

and then I hop into scripting the actual thing. I really don’t make any sections/dividers after that point, unless it’s a long script (+500 lines), but I do include comments on lines that I might find difficult to figure what they do later or sidenotes to know what to do or avoid.

Again, it really comes back to you. But hope this gave you an idea to work around!

5 Likes

Here how I organize my things in studio:

Here is how I organize my scripts

I normally make some “blocks” like under to seperate my code in 3 main category:
-----------------
– Services –
-----------------

The 3 main category I make are Services, Variables and core. I group the variables of the same kind togheter, e.g.: All players related variables will be in one block. In the core parts, I group all the function foo() one after one then all the events you may be using goes the same way.

Optimisation:
Obviously, optimisation is important when you make a big game, otherwise it may lags on lower-end devices! Optimisation can be as simple as making a variable for redondant objects/values, making a function for redondant code (something like 10 lines being copy pasted around your code, etc.

In ReplicatedStorage Replicated Storage
  • ServerEvents (Folder)
  • ClientEvents (Folder)
  • UI Assets (Folder)
  • Modules (Folder)
Content of each folder is self-explained by their name (:
Final note As they previously mentioned, everyone has their own way and their own preference on how they want to do it :slight_smile:

P.S.: Sorry for my bad spelling/grammar/non-sense

2 Likes

For organization I tend to do one of three things:

  • If I don’t have experience with a difficult problem then I will probably prototype and experiment to see if I can find a feasible approach

  • Make a working version and refactor as I go to reduce code repetition, encapsulate more, have better separation of concerns, etc

  • If I know what direction I am going in, I will put a lot more focus into designing an easy to use and extend interface up front as I will likely know enough risks and costs associated to related approaches

  • I try to nail my designs from the beginning and only do big sweeping refactors when I am 100% confident in a solution that I found. I minimize refactor time as much as I can by doing everything in my power to learn how to make my code better overall, largely by splitting up code based on functionality. Separation of concerns + encapsulation is a very powerful combination.

For optimization:

  • I hunt down the algorithm with the best computational complexity that fits the problem, or my particular implementation of the solution properly

  • If I feel like I have a specialized case where I could do better than a fast general algorithm, I’ll build towards that, but I’ll make sure it is sufficiently isolated from the rest of the code so that it doesn’t get in the way if it needs to be changed later down the road

  • I always make sure I make my code as fast as possible as long as it doesn’t sacrifice stability or maintainability. I never forgo performance if there is a proven better solution, because while premature optimization may be the root of all evil, no optimization can lead to an unusable product.

  • Never forget that there is no system that works in isolation. If you have five performance intensive systems and performance targets for each of those, aim for at least 5x the targets so they all hit their bar for speed in unison. Aim for more if you want to ensure that it works on lesser hardware or adaptable for worst case scenarios.

  • Aim to put the most effort into known performance sinks or performance intensive solutions. It is okay to optimize early if you know that a system will take a significant amount of processing time or will be used a significant amount throughout the project.

    • Things that would qualify are things like physics system, bulk AI updating, or even simple things that are used millions of times a frame like object dereferencing/lookup.

    • Don’t try to dump a whole lot of time and effort into optimizing something trivial like text display or mild operations that may be hit 1000 times per frame tops.

  • The best optimization is to not run the expensive code in the first place. By that I mean running inexpensive checks that can cull out irrelevant expensive computations that don’t lead anywhere. Things like checking collisions between objects halfway across the map from each other or making each zombie process all other zombies when they really only care about select targets like players.

And yeah, all of this gets easier with experience. You learn which paths to take from the start and you can skip a lot of work and prep time just by being familiar with the problem.

9 Likes