Organizing code for selecting units in an RTS game

I have been entertaining the idea of an RTS game for a while, now. Today I came upon a certain dilemma: code organization.

Specifically, I am working on the code that will manage selecting units. It works, but I’m not sure if it could be organized in a better fashion.

I’m not super inclined to reveal my code, but I’ll give some pseudocode on what it’s supposed to do, as well as elaborate on where my code is located:

local function selectUnit(unit)
	if the unit actually exists then
		add the unit to a list that stores all units currently selected by the player
		add a health bar that displays over the unit
		recalibrate the main screen of the command bar
		recalibrate the possible commands on the command bar
	end
end

local function deselectUnit(unit)
	if the unit is in the list of selected units then
		take the unit out of the list
		get rid of the health bar
		recalibrate the main screen of the command bar
		recalibrate the possible commands on the command bar
	end
end

local function deselectAll()
	(essentially just uses deselectUnit() on all units currently selected)
end

when the player clicks the left mouse button:
	if the mouse was aiming at a unit then
		selectUnit(unitHoveredOver)
	else
		deselectAll()
	end
end

when the player clicks the right mouse button:
	if the player has any units selected then
		invoke a remoteFunction to give the selected units their orders
	end
end

In my original setup, there is a local script in StarterPlayerScripts that handles the user’s mouse actions. When the local script finds that a unit has been clicked on, it calls on a module script to index the unit in a ‘selectedUnits’ list. That module script then calls on another module script to add the health bar to the player’s gui. (Both of these modules are in ReplicatedStorage, by the way.)

However, I discovered that I could also just put all the code, including binding the mouse to certain actions, into a single module script, thus reducing the local script to hardly more than a line or two of code (to call on said module). I can’t see any immediate reason why this would be considered bad practice, but it’s such a jump from my original setup that I had to take a step back and think. What is the best setup? When is it appropriate to make another module script? Should I just put some local scripts in my GUI that automatically recalibrate the command bar?

If this changes anything, I have a loose personal guideline to avoid having any script with more than 200 lines (excluding comments). Like I said, though, it’s a loose guideline.

It’s really up to you.

Code organization is something that particular programmers could argue about until the end of time. With that in mind, take my response, along with any others you may encounter, with a large shaker of salt.

My personal method and criteria

I tend to subscribe to the “Object-Oriented Everywhere” mentality. I created my own base class that all objects in my game inherit from, and then make every object its own module script. The objects themselves have self-contained functions that deal with a single behavior. This makes organization really easy because every object has its own self-contained context within a given file. In other words, you will only find the functionality of the “Queue” object, and you will only find the “Enqueue” logic in the “Queue:Enqueue()” function.

If you are unfamiliar with object-oriented programming, you can organize your code by systems. I think your selection system organization is actually a good start. Everything related to unit selection is self-contained and doesn’t weave through multiple modules.

Remember: the goal is making the code easy to read, debug, and extend.

Some words on script length limits…

In my humble opinion, I don’t think script line limits are very useful.

It’s mainly because program length is such an arbitrary measurement of program complexity. I could have a eight-million line program, but if 7,999,999 lines of that program is just a large set of static data or constants, it isn’t a very complicated program.

Another analogy I’ve heard is that measuring project completion or complexity by line counts is like measuring an airplane’s completeness by its size. Using that logic, a very large glider is more complex than a twin-jet airplane even though the glider has no engine and more limited avionics.

Instead of using arbitrary measures, you should organize your code via functions and placing related logic in the same module. In my opinion, if your modulescript is broken into digestible, self-contained functions that build upon each other, it should be easy to follow despite the size. And similarly, if your code is short but not broken into digestible, self-contained functions, you will still have a rough time.

This is a lot of words to say I think that having a script only call one line of a large module is not just acceptable, but intended.

Finishing Thoughts

You aren’t going to nail project organization in the first attempt. Part of being a good software developer is taking the time to reengineer and improve existing systems. If you move further in your project and begin to see that your organization is causing problems, you should immediately stop and take the time to refactor your code. Otherwise, you’ll enter a race of ‘technical debt,’ where poor design will incur ever-expanding issues with your codebase.

There is no shame in tearing down a system and redesigning it if it isn’t working. If anything, it’s a sign of a good programmer.

1 Like

That’s what I was afraid of.

I’ve never had any luck with utilizing object oriented programming. In fact, I would even say it’s not efficient for what I’m trying to do with this game idea (maybe in the future I will make another thread to discuss that).

I’m not sure if there is a term for this sort of programming mentality, but I often will start coding something, get overwhelmed by the size of the script, and then create module scripts to spread it out a little more. That 200 line guideline wasn’t really related to the “wealth” of my program: it’s more because having to scroll up and down all the time to locate and understand my code gets old real quick.

That’s actually quite comforting. What I find myself doing is I create pseudocode (similar to what I did in this thread), and then try to make the actual code as close and as simple-looking as the sketch.

Shame? No. Frustration because it’s going to take more time than I anticipated? Yes.

Anyways, I appreciate your response to this post. I waited a bit to see if anyone else would reply, but my patience has grown thin. I will mark your response as the solution.

Thanks again. :slightly_smiling_face: