Basic ModuleScript Tutorial for Beginners

You should check out AlvinBlox’s tutorial instead here!


Jump to:

Tutorial

ModuleScripts are an excellent way to enforce the DRY principle of not repeating yourself.

ModuleScripts are often used for storing functions that you need to use across multiple scripts. Now, rather than creating the same function again and again and again across a multitude of scripts, it’d be much more prudent just to use ModuleScripts. In this tutorial, I’ll be covering how to use these scripts.


local module = {} -- notice how this is what a table looks like?

return module

This is what a ModuleScript looks like when you create one in Studio.

Let’s create a basic function.


local module = {} -- notice how this is what a table looks like?

function module.print(text) -- you can include parameters like you can in a normal function.
   print(text) -- prints text to console
end

return module

If you run this code, nothing will happen because it’s a function and it needs to be called before it will do anything.

ModuleScripts are brilliant for following the DRY principle because you can call them from different scripts using require().

Let’s have a look.

Create a new script and parent it to the same thing as the ModuleScript.


local Module = require(script.Parent:WaitForChild("ModuleScript")) -- insert this code into your script.

Running it like this won’t do anything.

So let’s call the function this time:


local Module = require(script.Parent:WaitForChild("ModuleScript")) -- insert this code into your script.

Module.print("Hello!")

If you run that code, in Output in Studio or the Developer Console > Server, you should be able to see “Hello!”.

Before I noted that the first line in a ModuleScript is like a table - this is because ModuleScripts are essentially function holders that can be accessed by other scripts.

This means that you should be able to run a for loop over the ModuleScript, returning the name of the function as the index, and the function itself as the value.

So there you go, now you have yourself a basic ModuleScript function:


-- MODULESCRIPT CODE --

local module = {} -- notice how this is what a table looks like?

function module.print(text) -- you can include parameters like you can in a normal function.
    print(text) -- prints text to console
end

return module

-- SCRIPT/LOCALSCRIPT CODE --

local Module = require(script.Parent:WaitForChild("ModuleScript")) -- insert this code into your script.

Module.print("Hello!")

You may also want to look at this article for more information on ModuleScripts:
ModuleScript

Edit:
It’s worth noting that ModuleScripts can return any one value, not just tables, but tables (if not functions) are typically the most useful thing to reuse.

25 Likes

Nice article you have! You explain things very fluently. Here are my suggestions:

  • Since this article is all about ModuleScripts, you may wanna expand it as much as possible, what I mean is cover a lot about modulescripts. For example you didn’t go too deep into the return line in the module script, like saying that you can return anything you want, and not just tables specifically named module. That table could be called anything, and really you can put anything inside of it such as values with numeric indices or keys as well. Also, you could’ve mentionned Client ModuleScript vs Server ModuleScript, using method such as :IsServer(). You could also give examples of using modulescript, or give the intuition of making public modules (e.g. Datastore2)
  • As well out of curiousity, are there benefits from using _G instead for saving values?
    I use modulescripts to store things like inventories, it’s very ideal and with some metatable magic I can append some methods to them like :AddItem(). Else I would also use a folder inside of ServerStorage maybe. _G in general is a hacky way of storing values that you want to be accessed from other scripts. Primarily because that’s not what it’s for. It’s supposed to hold global variables (not in Roblox) and built-in lua functions
2 Likes
  1. I mean to say that this article was meant to be simple and to serve as a basic explanation of how ModuleScripts can be (and are generally in my opinion) used. Of course, you pointed out how I didn’t mention much on return which I think is a good point, so I’ll add it when I’m back on computer.
1 Like

I have few questions in the script waitforchild(“ModuleScript”)) why does require 2 perenticies

Why do we need to the write return module?

Modules need to return something. That’s how you are able to use a module after requiring it.

2 Likes

Thanks buddy REEEEEEEEEEEEreeeeeeee

Do note that a ModuleScript must return in any case, return nil if you don’t want to return anything (though in such cases typically, a module isn’t even necessary) .

Thanks, how would I do it for multiple functions? The module script is (in server script storage)

local module = {} -- notice how this is what a table looks like?

function module.Sword(Damage) -- you can include parameters like you can in a normal function.


	Tool = script.Parent
	Handle = Tool:WaitForChild("Handle")

	function Create(ty)
		return function(data)
			local obj = Instance.new(ty)
			for k, v in pairs(data) do
				if type(k) == 'number' then
					v.Parent = obj
				else
					obj[k] = v
				end
			end
			return obj
		end
	end

	local BaseUrl = "rbxassetid://"

	Players = game:GetService("Players")
	Debris = game:GetService("Debris")
	RunService = game:GetService("RunService")

	DamageValues = {
		BaseDamage = 7,
		SlashDamage = 10,
		LungeDamage = 30
	}

	--For R15 avatars
	Animations = {
		R15Slash = 522635514,
		R15Lunge = 522638767
	}



	Grips = {
		Up = CFrame.new(0, 0, -1.70000005, 0, 0, 1, 1, 0, 0, 0, 1, 0),
		Out = CFrame.new(0, 0, -1.70000005, 0, 1, 0, 1, -0, 0, 0, 0, -1)
	}

	Sounds = {
		Slash = Handle:WaitForChild("SwordSlash"),
		Lunge = Handle:WaitForChild("SwordLunge"),
		Unsheath = Handle:WaitForChild("Unsheath")
	}

	ToolEquipped = false

	
	for i, v in pairs(Handle:GetChildren()) do
		if v:IsA("ParticleEmitter") then
			v.Rate = 20
		end
	end

	Tool.Grip = Grips.Up
	Tool.Enabled = true

	function IsTeamMate(Player1, Player2)
		return (Player1 and Player2 and not Player1.Neutral and not Player2.Neutral and Player1.TeamColor == Player2.TeamColor)
	end

	function TagHumanoid(humanoid, player)
		local Creator_Tag = Instance.new("ObjectValue")
		Creator_Tag.Name = "creator"
		Creator_Tag.Value = player
		Debris:AddItem(Creator_Tag, 2)
		Creator_Tag.Parent = humanoid
	end

	function UntagHumanoid(humanoid)
		for i, v in pairs(humanoid:GetChildren()) do
			if v:IsA("ObjectValue") and v.Name == "creator" then
				v:Destroy()
			end
		end
	end
	local debounce = false
	local debounce2 = false
	function Blow(Hit)

		if not Hit or not Hit.Parent or not CheckIfAlive() or not ToolEquipped then
			return
		end
		local RightArm = Character:FindFirstChild("Right Arm") or Character:FindFirstChild("RightHand")
		if not RightArm then
			return
		end
		local RightGrip = RightArm:FindFirstChild("RightGrip")
		if not RightGrip or (RightGrip.Part0 ~= Handle and RightGrip.Part1 ~= Handle) then
			return
		end
		local character = Hit.Parent
		if character == Character then
			return
		end
		local humanoid = character:FindFirstChildOfClass("Humanoid")
		if not humanoid or humanoid.Health == 0 then
			return
		end
		local player = Players:GetPlayerFromCharacter(character)
		if player and (player == Player or IsTeamMate(Player, player)) then
			return
		end
		UntagHumanoid(humanoid)
		TagHumanoid(humanoid, Player)
		if humanoid.Name == "Health" then return end

		if debounce == false then return end
		if debounce2 == true then return end
		debounce2 = true
		if humanoid.Health - Damage <= 0 then
			if humanoid.Parent:FindFirstChild("leaderstats") then

				local plr = Players:GetPlayerFromCharacter(script.Parent.Parent)
				--	print(plr.Name)
				plr.leaderstats.Score.Value += math.round(humanoid.Parent.leaderstats.Score.Value/2)
				if humanoid.Parent.Name == "Spider" then
					local spiderstring = game.ReplicatedStorage.String:Clone()
					spiderstring.Parent = plr.Backpack
					local spiderstring = game.ReplicatedStorage.String:Clone()
					spiderstring.Parent = plr.Backpack
				end
				if humanoid.Parent.Name == "Wolf" then
					local spiderstring = game.ReplicatedStorage.WolfFur:Clone()
					spiderstring.Parent = plr.Backpack
					local spiderstring = game.ReplicatedStorage.WolfFur:Clone()
					spiderstring.Parent = plr.Backpack
				end
			end
		end

		humanoid:TakeDamage(Damage)	
		wait(0.25)
		debounce2 = false
	end



	function Attack()
		Damage = DamageValues.SlashDamage
		Sounds.Slash:Play()

		if Humanoid then
			if Humanoid.RigType == Enum.HumanoidRigType.R6 then
				local Anim = Instance.new("StringValue")
				Anim.Name = "toolanim"
				Anim.Value = "Slash"
				Anim.Parent = Tool
			elseif Humanoid.RigType == Enum.HumanoidRigType.R15 then
				local Anim = Tool:FindFirstChild("R15Slash")
				if Anim then
					local Track = Humanoid:LoadAnimation(Anim)
					Track:Play(0)
					Track:AdjustSpeed(2)
				end
			end
		end	
	end


	Tool.Enabled = true
	LastAttack = 0

	function Activated()
		if debounce == true then return end
		debounce = true
		if not Tool.Enabled or not ToolEquipped or not CheckIfAlive() then
			return
		end
		Tool.Enabled = false
		local Tick = RunService.Stepped:wait()
		if (Tick - LastAttack < 0.5) then
			Attack()
		else
			Attack()
		end
		LastAttack = Tick
		--wait(0.5)
		Damage = DamageValues.BaseDamage
		local SlashAnim = (Tool:FindFirstChild("R15Slash") or Create("Animation"){
			Name = "R15Slash",
			AnimationId = BaseUrl .. Animations.R15Slash,
			Parent = Tool
		})

		local LungeAnim = (Tool:FindFirstChild("R15Lunge") or Create("Animation"){
			Name = "R15Lunge",
			AnimationId = BaseUrl .. Animations.R15Lunge,
			Parent = Tool
		})
		Tool.Enabled = true
		wait(0.25)
		debounce = false	
	end

	function CheckIfAlive()
		return (((Player and Player.Parent and Character and Character.Parent and Humanoid and Humanoid.Parent and Humanoid.Health > 0 and Torso and Torso.Parent) and true) or false)
	end

	function Equipped()
		Character = Tool.Parent
		Player = Players:GetPlayerFromCharacter(Character)
		Humanoid = Character:FindFirstChildOfClass("Humanoid")
		Torso = Character:FindFirstChild("Torso") or Character:FindFirstChild("HumanoidRootPart")
		if not CheckIfAlive() then
			return
		end
		ToolEquipped = true
		Sounds.Unsheath:Play()
	end

	function Unequipped()
		Tool.Grip = Grips.Up
		ToolEquipped = false
	end

	Tool.Activated:Connect(Activated)

	Tool.Equipped:Connect(Equipped)
	Tool.Unequipped:Connect(Unequipped)

	Connection = Handle.Touched:Connect(Blow)
end

return module

and the sword script (inside the sword, server script) is

local Module = require(game:GetService("ServerScriptService").ToolModules.SwordScript) -- insert this code into your script.

Module.Sword(5)

Thanks!

You should probably break your sword down into multiple functions under a single module. I would recommend utilising Pseudoclasses for your sword.

Here’s some pseudocode (you will need to apply it to your own code):

local Sword = {}
local swordModel = ServerStorage.Tools.Sword
Sword.__index = Sword

function Sword.new(damage)
    local sword = setmetatable({}, Sword)
    local tool = swordModel:Clone()

    sword.damage = damage
    sword.tool = tool

    return sword
end

function Sword:Hit(humanoid)
     humanoid:TakeDamage(self.damage)
end

return Sword
1 Like