Decoupling game logic and UI

I’m building the structure of a game and I’d like to keep the game logic and UI logic separate. I understand that in other software projects, it’s common to follow design patterns to handle this kind of structure.
My question is: How is this usually done in Roblox? Or maybe I’m over-abstracting things?

One approach I’ve considered is using BindableEvents (or a custom Signal module), but I’m not sure how effective this is. For example:

Game logic script:

local UIS = game:GetService("UserInputService")

local Bindable = script.Parent.Bindable

UIS.InputBegan:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then
		return
	end
	
	Bindable:Fire(input.KeyCode)
end)

UI Script:

local Bindable = script.Parent.Bindable

Bindable.Event:Connect(function(key)
	-- Update UI
end)
1 Like

Worst idea ever both perfomance and logic wise.
You are calling deffered thread from already deffered thread.
Whatever you are doing is dumb and you should stop.
You should go for perfomance of produced bytecode and not some “copying code design”.

1 Like

I never said I’m copying code designs; I’m just looking for suggestions or the usual way GUI is handled in Roblox. To me, separating responsibilities makes perfect sense, but I take your reply with a grain of salt…

1 Like

I mean, your approach isn’t bad, if your handling everything client side. But what I usually do is use one local script, parented to the actual ScreenGui not a child of it(e.g. Frame or TextLabel). And some modules under that local script if I want to be a little more organised. And I just the whole UI logic for that ScreenGui in there. And just use a networking library or RemoteEvents to connect the client side to the server side.

2 Likes

Just organize order in which you connect logic.
Also bindableevent is not really meant to be used for that.

1 Like

It’s for communicating across scripts; it’s being used correctly in this case regardless of what it’s communicating.

To answer the OP’s question, from the perspective of a typical Roblox developer, you’re definitely over-abstracting this for little to no benefit. However, there’s nothing stopping you from doing it this way. If you want to make it similar to other design patterns you’ve seen, then go ahead. It’s not a wrong way to approach this. There’s a reply above that explains how they organize their UI logic, and I do so differently, so there’s not exactly a clear cut way to do it either.

2 Likes

Not really.
It was 100% useless up untill recently.
Right now it can be used for communication from sandbox to non sandboxed scripts. Script capabilities | Documentation - Roblox Creator Hub
You don’t need an entire event management system to call a function; call it dirrectly instead and store referance inside modulescript for example.

2 Likes

Yeah but it still can be used by non sandboxed scripts to non sandboxed scripts and still has some good use cases, especially if your scripting style doesn’t heavily rely on ModuleScripts.

Also, the sandbox feature is rather new and isn’t something I have actually looked at until now, so thank you for providing that!

2 Likes

This feature can be used for modding btw.

BindableEvent serializes and deserializes data every time so its not wroth using it at all.
Either use custom event library or call function dirrectly for optimization.
Afaik bindablevent is also used in parallel luau often.

Basically bindableevent is just a way to sanitize data of some sort.

3 Likes

You should definitely aim at making your UI less dependent on game state, but I agree that using signals (especially deferred ones) would not be appropriate here.

What I personally do when I have the option is similar to the Model-View-Controller pattern. In essence, you have a model (centralized abstract state), a view (the rendered UI tree) and a controller (updates the model and view based on each other).

There’s a billion ways you could apply this, but I personally like to split up all the ‘fanfare’ logic of updating and animating the UI tree in separate module interfaces. Each module is solely responsible for a specific set of UI components (usually grouped in a folder), which allows you to focus solely on how the UI should behave, and not when/how. I then have one script (or module in a single-script architecture) which single-handedly stores and updates the relevant state based on user input by interacting with these component interfaces.

I honest-to-god think this is a massively superior approach than anything a UI framework could provide. Frameworks come from a web background where the industry was basically forced into using them because of how awful the DOM API was. Before frameworks, we had MVC for web applets, and it just worked.

2 Likes

Thanks everyone for your answers. Taking a bit from each, I now have an idea of how to approach it, so I’ll put it into action and see how it goes :smiling_face_with_sunglasses:

1 Like

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