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)
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”.
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…
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.
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.
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.
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!
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.
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.