Script Architecture

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:

  1. First, create a Block model in ReplicatedStorage:
    1. Insert a Model in ReplicatedStorage. Rename it to Block.
    2. Insert a Part in Block. Rename it to Torso.
    3. Insert a Humanoid in the Block Model.
  2. Insert a Script in the Block model named Movement.
  3. Insert a Script in ServerScriptService named BlockGenerator.

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 BlockGenerator
local block = game.ReplicatedStorage.Block
-- Clone the Block models and let their own scripts handle movement logic
for i = 1, 10 do
block:Clone().Parent = workspace
end

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 Movement
local characterModel = script.Parent
local humanoid = characterModel.Humanoid
local target = workspace:WaitForChild("Target")
while true do
wait(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:

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:

  1. Create a Block model in ReplicatedStorage:

    1. Insert a Model in ReplicatedStorage. Rename it to Block.
    2. Insert a Part in Block. Rename it to Torso.
    3. Insert a Humanoid in the Block Model.
  2. Insert a Script in ServerScriptService named BlockManager.

  3. 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:

  1. Open a Place.
  2. In the Test tab on top, click Play to spawn your Player and Character.
  3. Open the StarterPlayer container in the Explorer window.
  4. 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.