Roblox experiences can contain thousands of lines of code. The architecture of an experience is its organization and structure. Multi-script architecture and single-script architecture are two common approaches to organizing code.
Multi-script architecture uses multiple Scripts and LocalScripts to run all game mechanics.
Single-script architecture uses multiple ModuleScripts, a single Script, and a single LocalScript to run all game mechanics.
You can choose one or combine both multi-script and single-script architecture to create your Roblox experiences.
Multi-Script Architecture
Multi-script architecture uses multiple Scripts and LocalScripts to run all game mechanics. Choose it if you're new to Roblox Studio and want to create games fast.
Multi-script architecture has the following advantages:
- You can experiment with a variety of powerful features Roblox Studio offers.
- You can use public models from the Marketplace without adjusting their scripts.
- It's easier to find errors in scripts because the errors are contained within each script.
- It's easier to share scripts and isolated models with other developers.
Multi-script architecture has the following downsides:
- It can be challenging to track multiple scripts running independently because unpredictable behaviors might appear if multiple scripts modify the same Roblox object in a short time window, and you have little control over the code execution order.
- It promotes cloning copies of the same scripts for repeated contexts, thereby running multiple copies, which is inefficient and can reduce performance.
- It forces you to rely on the properties and attributes of objects, also known as the Roblox DataModel, as the source of truth for the game state.
Block Spawners with Multi-Script Architecture
To use multi-script architecture to spawn multiple Block models that move randomly:
- First, create a Block model in ReplicatedStorage:
- Insert a Part in Block. Rename it to Torso.
- Insert a Humanoid in the Block Model.
- Insert a Script in the Block model named Movement.
The Script named BlockGenerator clones multiple copies of the Block model in ReplicatedStorage to the Workspace. Each Block model gets its own copy of the Movement script, making this a multi-script application.
-- Script in ServerScriptService named BlockGeneratorlocal block = game.ReplicatedStorage.Block-- Clone the Block models and let their own scripts handle movement logicfor i = 1, 10 doblock:Clone().Parent = workspaceend
The Script named Movement handles the movement for each Block. It uses its own control logic loop to update each Block's position. This multi-script approach means each Block can move separately from other Blocks. If one Block's Movement script has errors, it doesn't affect Movement scripts of other Blocks.
-- Script parented to each Block named Movementlocal characterModel = script.Parentlocal humanoid = characterModel.Humanoidlocal target = workspace:WaitForChild("Target")while true dowait(math.random()*5)local movePosition = workspace.Target.Position + Vector3.new(math.random(-40, 40), 0, math.random(-40, 40))humanoid:MoveTo(movePosition)end
Single-Script Architecture
Single-script architecture uses multiple ModuleScripts, a single Script, and a single LocalScript to run all game mechanics. Choose it if you're an experienced developer and want greater control over code performance, reusability, and modularity.
Single-script architecture has the following advantages:
- You can create and test new features while minimizing the chance to break existing features.
- It's easier to use ModuleScripts to simplify Script Communication.
- You can follow powerful software development patterns such as event-driven programming.
- You can store data outside the Roblox DataModel, such as in variables.
Single-script architecture has the following downsides:
- It can be challenging to create mental models of your ModuleScript hierarchy.
- It's more difficult to debug chains of ModuleScripts than separate Scripts and LocalScripts, and an error in one ModuleScript can be fatal to your game if you don't handle them properly.
- It's more difficult to share scripts and isolated models with other developers.
Block Spawners with Single-Script Architecture
To use single-script architecture to spawn multiple Block models that move randomly:
Create a Block model in ReplicatedStorage:
- Insert a Part in Block. Rename it to Torso.
- Insert a Humanoid in the Block Model.
Insert a Script in ServerScriptService named BlockManager.
Insert a ModuleScript parented to BlockManager called Block.
The Script named BlockManager generates each Block by requiring the ModuleScript named BlockModule, which handles the movement of each Block. Single-script architecture uses a ModuleScript with a single loop to update each Block. This eliminates duplicate scripts for each Block that may result from cloning the Block's scripts in multi-script architecture.
-- Script in ServerScriptService named BlockManager
local Block = require(script.BlockModule)
-- Create and keep track of all Block characters
local blockList = {}
for i = 1, 10 do
local newBlock = Block.New()
table.insert(blockList, newBlock)
end
-- Updates all the blocks together to avoid creating duplicate scripts
game:GetService("RunService").Stepped:Connect(function()
for _, block in next, blockList do
block:Step()
end
end)
The ModuleScript named BlockModule manages the generation and movement of each Block. The ModuleScript uses object oriented programming to encapsulate the Block and its movement logic so each Block moves on its own even though there's a single loop in the BlockManager script. This emulates the same separation between Blocks that a multi-script approach can provide.
-- ModuleScript parented to BlockManager named BlockModule
local BlockModule = {}
BlockModule.__index = BlockModule
function BlockModule.New()
local self = setmetatable({}, BlockModule)
-- Clone new BlockModule model
self._blockModel = game.ReplicatedStorage.Block:Clone()
self._blockModel.Parent = workspace
-- Internal movement update timer
self._nextMoveTime = 0
return self
end
function BlockModule:Step()
local t = os.clock()
-- Checking every frame when to move again
if self._nextMoveTime > t then
return
end
local movePosition = Vector3.new(math.random(-40, 40), 0, math.random(-40, 40))
self._blockModel.Humanoid:MoveTo(movePosition)
self._nextMoveTime = t + math.random()*5
end
return BlockModule
Combined Approach
In practice, you are likely to use elements of both multi-script and single-script architecture when building a Roblox experience. You can treat the two architectural concepts as sets of tools rather than strict templates to follow.
The default StarterPlayerScripts container has examples of both multi-script and single-script approaches to script architecture. To open the StarterPlayerScripts in Studio:
- Open a Place.
- In the Test tab on top, click Play to spawn your Player and Character.
- Open the StarterPlayer container in the Explorer window.
- Open the StarterPlayerScripts container.
The default StarterPlayerScripts container has multiple LocalScripts named BubbleChat, ChatScript, and RbxCharacterSounds to manage the default behavior for rendering the bubble chat, the chat GUI, and character sounds. These scripts are examples of multi-script architecture because they run independently of each other to achieve different mechanics.
The PlayerScriptsLoader is an example of single-script architecture because its one line of code loads the PlayerModule ModuleScript, which loads two other ModuleScripts named CameraModule and ControlModule to achieve the default behavior for controlling the Camera and the Character.
Notice that StarterPlayerScripts combine elements of the multi-script and single-script approaches to architecture. Adopting a hybrid approach to architecture can help you achieve balance between the tradeoff of complexity and ease of organization.