What are your best practices?

User Interface is incredibly important to creating a game that looks great, feels great to play, and is usable by it’s target audience. To this end, you should consider these things when creating your UI.

[ul]
[li]Do the colors go well together? See this website for ideas, and this website for guidelines.[/li]
[li]Does it look good, does it fit together well? Consider using icons from various resource websites to help you. The composition of your user interface should take into account the purpose of your game, for example if you are making an RPG there should be a clear area where you can access the most important functions such as inventory, friends, settings, character.[/li]
[li]Does it look good on all compatible devices? If your game is aimed towards both mobile and desktop then you must consider that when making your interface. ROBLOX Studio has a testing tool for this.[/li]
[li]Is it easy to use? If you ask a couple of friends to test your user interface, and they struggle to acomplish tasks that you set them, then it’s likely that your design isn’t going to be easy for other players too.[/li]
[li]Does your interface give feedback? It’s extremely useful for players when a GUI element responds to their input. It makes them aware that what they’ve just done had an affect on the game. If you purchase an item, then perhaps have a confirmation window to inform them exactly what they have purchased.[/li]
[/ul]

Other Tips

[ul]
[li]Take advantage of the ROBLOX engine. Experiment with different things until you can get the best possible outcome.[/li]
[li]Consider Lighting and Building to further engage/immerse your users into the game.[/li]
[li]Make use of plugins to aid you while developing. There are hundreds of useful tools at your disposal, why make things harder?[/li]
[li]Work with friends or other developers if you can’t do it yourself. People are talented at different things. If you’re an amazing builder and scripter, but you’re not great at graphic design, then get someone who is to help you out.[/li]
[li]Feedback and testing is very important. Make sure you get rid of any game-changing/breaking bugs as well as tweaking to make sure your game is as polished as it can be.[/li]
[li]Never stop developing, you can only get better.[/li]
[li]Consider new features such as Filtering Enabled and Physics Solver. These will change the way your game works massively.[/li]
[li]Make a game that you would enjoy to play yourself. It will motivate you and you will know exactly what you want to do.[/li]
[li]Not everyone has a super-computer, you need to take into account the varying levels of power your users will have. Don’t make something too intensive for low-end machines or they won’t play your game.[/li]
[/ul]

7 Likes
  • Design before coding. I never just blindly code anything without a game-plan. Otherwise the result is a bunch of spaghetti code.

  • Reuse working code from past projects/games. Don’t rewrite what you don’t need to. But always find ways to improve the code if possible.

  • Avoid optimization unless absolutely necessary. Everyone is obsessed by making code as optimal as possible, but sometimes you don’t need that and you end up just cluttering your code base. The only times I’ve ever ran into situations where I’ve needed to optimize code severely in ROBLOX is when dealing with terrain generation. But that’s because you’re doing millions of iterations as quickly as possible. Trying to do 100,000 cycles without yielding is tough, and sometimes requires that optimization. But usually your code isn’t doing thousands of operations at once.

  • Don’t copy-and-paste code from other people or websites without fully understanding what it is doing. Otherwise you’re cheating yourself and are not learning. You should understand how and why your code works.

  • Stress your code in all ways. Make sure your code can handle as huge of a load as it might experience with full servers and thousands of people online at once. Also, be sure to test as if you’re a 7-year-old user: Treat your program/game like a piece of junk; throw it into the ground and step on it; TRY TO BREAK IT.

  • Add comments. Oh my goodness, this is the most neglected coding feature of all time. WRITE COMMENTS. Please, just do it. You’ll be helping your future-self and anyone else who ever has to touch your code. This can be as simple as documenting what a certain object does, what its methods are, and what those methods return. It doesn’t have to describe what every line is dong.

13 Likes

Some thoughts on commenting and documenting code. Yes comments are important, particularly for complex code, but I generally fall into the camp that code should be self documenting. I’ll explain what that means with an example.

I’ve been working on some physics stuff for a top seceret ROBLOX game idea I have (It’s a fully 3D team based remake of the old Joust game. I’m terrible at keeping secrets). In my research I found some useful JavaScript code that is almost incomprehensible. It looked, in part, like this:

function a6(a){
	vv0 = fh.v0;
	fh.vf = vv = Math.sqrt(vv0 * vv0 + 2 * a * fh.x);
	fh.t = (vv - vv0) / a;
	fh.va = .5 * vv0 + .5 * vv;
	fh.vf = v
}

That nearly incomprehensible chunk of code works, and it sets the acceleration variable on an object and adjusts other values to reflect the change so that you can tell, for example, how long the object will move and how fast it’s going to be going over a set amount of distance. At first you’d think this code needs a ton of comments, and that would certainly help. But here’s my first pass at converting this mess to Lua:

function PHYTool:calculateNewAcceleration(newAcceleration) 
	local initialVelocity= self.initialVelocity
	local finalVelocity = math.sqrt(initialVelocity* initialVelocity+ 2 * newAcceleration * self.distance)
	self.finalVelocity = finalVelocity
	self.time = (finalVelocity - initialVelocity) / newAcceleration
	self.averageVelocity = .5 * initialVelocity+ .5 * finalVelocity
	self.finalVelocity = finalVelocity
end

As you can see just by naming the variables sensibly and not even modifying the logic the function is much more understandable. This is the version that I used while verifying that the code was working and through testing of my object. Once I was satisfied that it was working, I rewrote it. Here’s the same chunk of code functionality (the ‘set’ functions live further up in the file so they can be used by other functions):

function PHYTool:setTime(acceleration, finalVelocity, initialVelocity)
	self.time = (finalVelocity - initialVelocity) / acceleration
end

function PHYTool:setAverageVelocity(initalVelocity, finalVelocity)
	self.averageVelocity = .5 * initalVelocity + .5 * finalVelocity
end

function PHYTool:setFinalVelocity(acceleration, initalVelocity, distance)
	self.finalVelocity = math.sqrt(initalVelocity * initalVelocity + 2 * acceleration * distance)
end

-- Sets a new constant acceleration and recalculates a new finalVelocity, time, and averageVelocity accordingly
-- sets: 
--   acceleration
-- modifies:
--   time
--   finalVelocity
--   averageVelocity
function PHYTool:calculateNewAcceleration(acceleration) 
	self:setFinalVelocity(acceleration, self.initialVelocity, self.distance)
	self:setTime(acceleration, self.finalVelocity, self.initialVelocity)
	self:setAverageVelocity(self.initialVelocity, self.finalVelocity)
	self.acceleration = acceleration
end

Much better. The ‘set’ functions don’t really need comments because what they do is self evident given their names and parameters. The code in the ‘calculate’ function is also pretty self explanatory. The ‘calculate’ function itself needs documentation because it has side-effects, that is it modifies variables outside of it’s own scope which isn’t obvious by the functions name. By the rules of self documenting code the function would need to be called something like ‘calculateAccelerationAndUpdateTimeFinalVelocityAverageVelocity’ but that was just too wordy for me.

BUT, if I did use that long name then the code calling it could tell exactly what was going on. Compare what I have now:

	local tool = libs.PHYTool.new(targetHeight - pos, velocity, acceleration)
	[...]
	tool:calculateNewAcceleration(newAcceleration)
	if (tool.time < 0) then
		-- do stuff
	end

with

	local tool = libs.PHYTool.new(targetHeight - pos, velocity, acceleration)
	[...]
	tool:calculateAccelerationAndUpdateTimeFinalVelocityAverageVelocity(newAcceleration)
	if (tool.time < 0) then
		-- do stuff
	end

In the first example it isn’t obvious that tool.time is updated by calculateNewAcceleration(), but in the second one you know exactly whats going on. The important take-away being that information given in comments isn’t readily available in the code that’s using your functions. I should probably rename those functions to the longer form after all. (but I wont remove the comments even if I do, because of those side effects which need to be called out).

This doesn’t mean to not comment code. Complex code, code with side effects, and code that is doing things in non obvious ways should always be commented, if not for the benefit of other people then for your own good when you come back months (or even days) later and forget what you did and why.

9 Likes

I’m not kidding though. Deliberately work on your project. Deliberate practice is the only practice that works.

5 Likes

I suggest putting in a few words about how to properly split tasks between server/client. Especially in older models (but new ones as well), the server is doing all the heavy lifting and the client only changes the mouse icon. A lot of gear items are guilty of this as well.

There are also cases in which the client does all the work, which then doesn’t work with FilteringEnabled.

I would also mention that while code shouldn’t be optimized unless absolutely necessary, networking should be optimized as much as possible. People have horrible internet connections, especially on mobile.

Fantastic advice so far, keep it coming!

One thing in particular I’d like to bring up: are there any old habits that you’ve broken or dated practices that you see on ROBLOX that you have good solutions for?

1 Like

One of the things I find particularly annoying with FilteringEnabled is the when people think they still have to store client-sided objects in the Camera.

It’s also best not to store client-side resources in Lighting, rather than ReplicatedStorage.

In addition, server-side assets intended for the server and the server only should always be stored in ServerStorage, otherwise they risk being found.

1 Like

[quote] Fantastic advice so far, keep it coming!

One thing in particular I’d like to bring up: are there any old habits that you’ve broken or dated practices that you see on ROBLOX that you have good solutions for? [/quote]

Overcomplicating systems, creating too many functions that do everything and can be multi-purposed (Just made project more overwhelming to imagine as a whole in your head; keep it manageable and don’t worry about using one off functions or being not perfect in performance)

When it comes to building:

Uneven ground looks awesome.

Distribute detail effectively. Don’t have large flat areas of nothing, and then a fence that uses 3 new parts for a post + connecting pieces of wood that attach it to its neighbouring post.

DON’T start out with a giant baseplate and start building very delicately from one corner. Build the rough outlines. Like, if you’re making terrain, make the main big hills, mountain borders, landmarks… Once you’ve got that, slowly begin to fill in the details. With this method, you can, in theory, stop at any point once you’ve added in your landmarks and be done with it.

I don’t think I’ve ever seen a meticulous build finished to completion, besides a few showcase games. (Those come with their fair share of empty “WIP” areas however)


Focus on the big picture, get into the details after. Minimal viable product.

2 Likes

Make the most of plugins
Plugins like Crazymans Bezier curves, qCmdUtl, FromLegoUnivere’s Weld, Motor6D,and Prism creators and triangle plugins can dramatically increase the quality and speed of your building. It seems obvious to us, but alot of new builders don’t really use plugins.

Always practise good model grouping and naming, it can really help when going back to old parts of your projects for both building and script editing. Back in the old days I just stuck objects in lighting and rarely grouped anything. Everything is now an absolute mess and can really slow down development sometimes.

Lighting is a very tricky thing but even small changes can have huge impacts on the look and feel of your levels. Reading into general lighting theory is a fantastic thing to do. Lighting in game environments - the hows and whys tutorial - ModDB

3 Likes

CoughAxisAngleCough

1 Like

Very nice, I particularly like the comments on game design, something we don’t have much mention of on the Wiki. Along with the above prompts, I think it would also be nice to have advice on organization. How do you organize your Workspace? RemoteEvents? ModuleScripts? Code in general?

1 Like

Remotes:
A folder in replicatedstorage named Remotes
Occasionally with subfolders

Modules:
A folder in either server or replicatedstorage named Modules

I also never use the default character spawning system as my own system gives me more control over how characters spawn, what goes into their playergui when they spawn, etc.

2 Likes

When it comes down it it, the Workspace should be organised based on what you need particular things for. For example, projectiles should have a Folder called Projectiles, that the server can parent projectiles to (this would include canons, bullets, arrows, lasers, etc). You would have a folder also for any map elements that don’t need interaction, or health, things like that. I usually call this StaticModels or Map it’s entirely up to personal preference. Folder for other more important things, like perhaps item drops should also have their own folder. I think it’s really important to keep the workspace as minimal as possible on the upper hierarchy, otherwise you’re going to have a huge list of miscellaneous items that you don’t need to see.

Similarly, ReplicatedStorage and ServerStorage should follow this rule. You don’t want to put everything directly as a child and hope for the best. If you have multiple assets, then why not create a folder called Assets that would parent sub-folders like Models, Sounds and Items to help better organise. This will also help when it comes to accessing these via Scripts and LocalScripts, because you know immediately where things are.

ServerScriptService might be a little different, but it essentially follows the same rules. Say you have a Script called ChatService then create a script inside of ServerScriptService, name it, and if you are using ModuleScript’s, put them as a child of it.

Overall, I think it’s basically providing yourself with the most sensible layout, where things are properly named, properly stored and you know where to find things easily.

Lua is a whole different story. There’s no right or wrong answer, as far as writing code goes. People have various different preferences on how to write, name and structure code. This isn’t to say there are recommended practices though, I personally think that when writing code you should always know what something does. When you’re reading it, it should make sense. If you have one function to turn on a light, then name it appropriately.

local function TurnOnLight() -- Light turns on end

And as I said earlier, you should always keep up-to-date with ROBLOX Lua/API so you’re not doing things like

Part:remove()

to permenantly get rid of something.

3 Likes

ReplicatedStorage
I put all of my RemoteEvents/RemoteFunctions in organized folders within a folder called “Network”.
I also use ReplicatedStorage for any assets I need to store for the client. (mostly ModuleScripts)

ServerScriptService
I have one script in ServerScriptService – everything uses ModuleScripts. I try to organize things neatly, by category. As a rule of thumb (and this is the same for any of my organization strategies), I try not to have more than 10 objects in a single folder.

ServerStorage
I have few uses for ServerStorage, albeit the uses I have make ServerStorage tremendously useful. I use the same organization structure here as aforementioned.

It’s almost always good practice to keep client-only instances under the camera, regardless of whether filtering is being used. It maintains compatibility between FE and non-FE, even if only for debugging purposes.

Apart from that, here’s my two cents:

  • Check your framework privilege. Don’t over-organize, and make sure you understand your problem thoroughly before adding another layer of abstraction to the jumble. Form follows functionality.
    In fewer words, not everything has to be generic.
  • Don’t be afraid to illustrate what you want to do before you start writing code, especially if you’re visually-oriented like me. Whiteboards are awesome.
  • I’d say something about separating server and client duties but I feel like this has already been hashed to death.
1 Like

There’s no reason to make your FE game compatible with filtering disabled… :confused:

1 Like

Sometimes it’s the best practice though, proper client-server communication and all that jazz. When I made the train system for Paper Mario RP, it was FE-compatible without any modifications because that was the right way to do it, I didn’t even try nor realize I was making it FE-compatible.

My best practices:
cmdutl
cmdutl
cmdutl

The tool is not prone to large rounding errors like the studio tools, it is extremely precise because you can put in your increments of adjustment, and the best feature is that it will accept equations as inputs. My favorite part of the tool is that I can make angled pieces line up by putting in deg(atan2(a,b)) into the edge rotate field, and sqrt(a^2+b^2)-0.2 in the length field. cmdutl is the reason why my most recent locomotive works so well in the old solver despite its mechanical complexity, and the same for PSGSolver, which doesn’t allow slop in joints like the old solver does.

1 Like

[quote] I’ve been a vivid ‘console’ gamer for a long time (years now) so I’m use to a lot of controllers and controller layouts. I thought I’d share some guidelines after what I’ve seen and what I know. I’ve seen a lot of games fail because the controller layout was stupid as heck. Sure you can add controller support but if you make ridiculous bindings…you aren’t going to be popular.

[center]
External Media
[/center]

Don’t ever;
[ol]
[li]Use DPAD as steering or movement controls (If you do this, don’t even bother adding controller support. What is this? PS1) [Please see exception 1][/li]
[li]Use X,Y,B,A as movement/steering [/li]
[li]Use Start, Back, Select controls as main parts of the game (e.g. having the start button fire a bullet. I’ve seen it and you should not do this, please for the love of god)
[li]Use thumbstick presses as a means of pausing (My thumbs hurt, stop) [/li]
[/li]
[left]Just please, never do any of these.[/left]

Acceptable; however, not the best
[ol]
[li]Use Thumbstick 2 (bottom right) as movement/steering (Some people actually prefer this, it’s called ‘Southpawing’)[/li]
[li]Use the ‘A’ button as a means of accelerating a car (seriously, you have triggers. Why?) [/li]
[li]Use Thumbstick 2 as a accelerator (What about camera controls? Camera controls generally use Thumbstick2) [/li]
[li]Use the Left trigger as a means of accelerating/braking (This should be on the right, it’s very common practice) [/li]
[/ol]

Good controller layout practices
[ol]
[li]Using the right trigger (R2) as a means of accelerating/shooting (It’s becoming common practice in a lot of layouts)[/li]
[li]Using the R1 button as a handbrake (Better feel than using X for example, also closer to accelerator)[/li]
[li]Using the L2 button as a brake (More precise braking and/or reversing)[/li]
[li]Using Thumbstick 1 as movement or steering (This is a ‘Northpaw’ layout)[/li]
[li]Using Thumbstick 2 as camera control (This is a ‘Northpaw’ layout)[/li]
[li]Using Start as a means of starting the game or pause it (This should be self explanatory)[/li]
[li]Using A to navigate/select in addition to the DPAD controls (More precise selection)[/li]
[li]Using X, R1 or B to reload a weapon (A lot of people associate ‘X’ with reloading and this is becoming common in new games, X/B is preferred)[/li]
[li]Using B as a melee attack button (it’s close to the edge, shortest travel time of your thumb for quickness)[/li]
[li]Using Y to interact with something (Walk through the door Niko!)[/li]
[li]Reminding players what button does what (e.g. GTA 'Press Y to get in the car. You’ll notice these die down after awhile and there’s not as many or in some cases, at all)[/li]

Generally X,B,Y,A buttons can be used for anything :stuck_out_tongue:
[/ol]

Exceptions
[ol]
[li]If your character only moves in four ways or an isometric format, DPAD can be acceptable. Although you should use thumbsticks for better comfort [/li]
[/ol] [/quote]
Some rough practices for good controller support without driving your players crazy because you’ve binded the fire bullet button to select or the thumbstick to pause.
[ Original Thread ]

4 Likes

[quote=“Quenty, post:7, topic:18642, full:true”]
I write testable code, using this guide …[/quote]

Bravo Quenty! I personally would love to see an article authored by Quenty on how to bring testing practices to the ROBLOX environment. I like to test my code as well, but the ROBLOX environment is not ideal for it and I haven’t yet settled on a good strategy to accomplish it.

My best practices include using source control. I believe it to be an indispensable practice. Because the ROBLOX place file format makes it especially difficult to practice, I’ve been petitioning for change. (Please don’t mention ROBLOX’s version history as an implementation of source control - it has almost none of the features of a decent version control system.) In order to practice source control with a ROBLOX place file, I save it in the old rbxlx xml file format. Before committing any changes, I examine the differences made to the file to ensure I am not committing any changes that were not deliberate. This has saved my bacon numerous times. I also use descriptive comments when committing code to make finding past revisions easier. The shifting sands of the file format keep me on my toes and make this practice a challenge, but it is certainly worth it. It is particularly important when working in teams.

7 Likes

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.