Disclaimer
The code used isn’t the most efficient nor is the code protected against exploits. You will have to fix those on your own.
Ever wondered how the Minecraft crafting system works? How do they know which recipe corresponds to which object? How can you have different recipes for each object? These are all the questions I will answer in this tutorial.
Some helpful things
In this link, it states that there are 2 different types of recipes: shaped and shapeless recipes.
Crafting – Minecraft Wiki
“Some recipes do not require their ingredients to be arranged in a specific way on the crafting grid. These are commonly known as shapeless recipes. For example, players can craft a fermented spider eye by placing its ingredients anywhere within the grid.”
We will be focusing on only 1 type of recipe for simplicity: the shaped recipes. It’s easy to integrate both types into the system but it would make the tutorial harder to understand.
NOTE: This section would be very brief as this isn’t what the tutorial is about. You can skip all UI creation parts.
Creating the Crafting Table UI
Let’s start with creating the UI for the crafting table.
#1 . Start with a rectangular frame
Create a rectangular Frame with the borders removed:
#2 . Create a text label
Create a TextLabel on the top of the Frame and set the text to, “Crafting”:
#3 . Create 9 frames:
On the left side of the box, create 9 square image buttons and name them from 1 - 9:
#4 . Create 1 box on the right
Create an ImageButton on the right of the frame and name it “Result”:
#5 Create an arrow connecting the grid to the box
You might have noticed in Minecraft that every item in the game has its own “tag”, such as block.minecraft.dandelion or block.minecraft.allium . “block”, “minecraft”, “dandelion”, and “allium” are all different categories that can be used to determine what is what. dandelions and alliums can be considered as blocks, but they cannot be considered as the same flower. This is the logic we’re going to use for this tutorial.
Phase 1: Creating Individual Items and Data
Create a Folder in ReplicatedStorage named, “Items”. Create a ModuleScript named “Oak Planks” and insert it into the “Items” folder. Then, paste this code template into the ModuleScript :
return { -- Returns the data of the module
Tags = {
--[[
These will be the categories or tags in this instance
that will define what the object can be "considered" as
--]]
},
Image = "" -- This will be the image that will be displayed when the object is shown
}
Now let’s define the data for the “Oak Planks”:
return {
Tags = {
"Planks",
"Oak Planks"
-- The item, "Oak Planks" can now be considered as "Oak Planks" and "Planks"
},
Image = "rbxassetid://3465206430"
}
Now, create another ModuleScript named, “Spruce Planks”. Parent this module script to the “Items” folder. Insert this block of code:
return {
Tags = {
"Planks",
"Spruce Planks"
-- The item, "Spruce Planks" can now be considered as "Spruce Planks" and "Planks"
},
Image = "rbxassetid://152572250"
}
Notice how both the “Oak Planks” and “Spruce Planks” both have the tag “Planks”. This will tell our interpreter that both of these items are considered “Planks”.
Other items
Cobblestone:
return {
Tags = {
"Stone",
"Cobblestone"
},
Image = "rbxassetid://57539377"
}
Sticks:
return {
Tags = {
"Sticks"
},
Image = "rbxassetid://7248264418"
}
Furnace:
return {
Tags = {
"Furnace"
},
Image = "rbxassetid://133252646"
}
Wooden Axe:
return {
Tags = {
"Axe",
"Wooden Axe"
},
Image = "rbxassetid://137512749"
}
Phase 2: Creating Individual Recipes
Create a Folder in ReplicatedStorage named, “Recipes”. Create a ModuleScript named “Furnace” and insert it into the “Recipes” folder. Then, paste this code template into the ModuleScript :
return {
-- This is where the recipes will be
}
Now let’s add the crafting recipe:
return {
{
"Cobblestone", "Cobblestone", "Cobblestone",
"Cobblestone", nil, "Cobblestone",
"Cobblestone", "Cobblestone", "Cobblestone"
--[[
Notice how the layout of the grid is 3 by 3:
[1] "Cobblestone", [2] "Cobblestone", [3] "Cobblestone",
[4] "Cobblestone, [5] "nil", [6] = "Cobblestone,
[7] "Cobblestone", [8] "Cobblestone", [9] "Cobblestone"
This corresponds to the grid pattern when making the crafting
table UI. We named each ImageButton from 1 - 9.
--]]
}
}
Some items in Minecraft, such as axes, have multiple crafting recipes. Create a ModuleScript named “Wooden Axe” and set the parent to the folder named, “Recipes”. Then, insert this block of code:
return {
{ -- Our interpreter will consider both these tables as a recipe for the "Wooden Axe"
nil, "Planks", "Planks",
nil, "Stick", "Planks",
nil, "Stick", nil
},
{
"Planks", "Planks", nil,
"Planks", "Stick", nil,
nil, "Stick", nil
}
}
As you can see, there are 2 different sets of crafting recipes. One is the direct reflection of the other, and vice versa.
Phase 3: Crafting Table Initialization
For the sake of simplicity, clicking on each of the boxes of the crafting table would bring up all of our items. What we’re going to do now is hook each of the 9 boxes with a MouseButton1Click
event and bring up a list of all the different items.
We’re going to create the UI first, so here’s a summary of how to make it:
Adding a Creative Inventory UI
#1 . Create a ScrollingFrame
Create a scrolling frame to the left of the main crafting table UI:
#2 . Insert UIConstraints
Insert a UIGridLayout with a padding of 0, 10, 0, 10
and a UIPadding with a padding of 10
on all offset values. Parent these to the frame we just created:
#3 . Create an ImageButton
Create an ImageButton. Name it “Template”. This will be our template to show what we have in our “Inventory” (it’s like a creative inventory from Minecraft):
Now we’re going to create a LocalScript . Name it “Crafting” and parent this LocalScript to the ScreenGui:
Parent our “Template” ImageButton to the LocalScript we just created.
Now that we’ve created our UI and scripts, it’s time to take the first step and define all the different variables for our crafting system to work:
local RP = game:GetService("ReplicatedStorage")
local Items = RP:WaitForChild("Items")
local Recipes = RP:WaitForChild("Recipes")
local MainGui = script.Parent
local MainFrame = MainGui:WaitForChild("Main")
local CraftingMaterialsFrame = MainFrame:WaitForChild("CraftingMaterials")
local Result = MainFrame:WaitForChild("Result")
local Template = script:WaitForChild("Template")
local itemData = {} -- This is where all the item tables are stored
local recipeData = {} -- This is where all the recipe tables are stored
local craftingData = {} -- This is where the crafting table data is stored
local CurrentButton -- This is the button we're currently editing
After defining the variables, we can start writing code to store the data and give functionality to our UI:
local RP = game:GetService("ReplicatedStorage")
local Items = RP:WaitForChild("Items")
local Recipes = RP:WaitForChild("Recipes")
local MainGui = script.Parent
local MainFrame = MainGui:WaitForChild("Main")
local CraftingMaterialsFrame = MainFrame:WaitForChild("CraftingMaterials")
local Result = MainFrame:WaitForChild("Result")
local Template = script:WaitForChild("Template")
local itemData = {}
local recipeData = {}
local craftingData = {}
local CurrentButton
for _, itemModule in ipairs(Items:GetChildren()) do -- Loops through each module under the "Items" folder
local data = require(itemModule)
itemData[itemModule.Name] = data -- Storing each item in the "itemData" table
local newTemplate = Template:Clone() -- Cloning the template
newTemplate.Name = itemModule.Name
newTemplate.Image = data.Image
newTemplate.Parent = CraftingMaterialsFrame
newTemplate.MouseButton1Click:Connect(function() -- When this template is clicked
end)
end
for _, recipeModule in ipairs(Recipes:GetChildren()) do -- Loops through each module under the "Recipes" folder
recipeData[recipeModule.Name] = require(recipeModule) -- Storing each recipe in the "recipeData" table
end
for index = 1, 9 do
local Button = MainFrame:WaitForChild(tostring(index)) -- Hacky way to get each button slot in the crafting table
Button.MouseButton1Click:Connect(function()
end)
Button.MouseButton2Click:Connect(function()
end)
end
Result.MouseButton1Click:Connect(function()
end)
Now let’s add functionality to our buttons:
local RP = game:GetService("ReplicatedStorage")
local Items = RP:WaitForChild("Items")
local Recipes = RP:WaitForChild("Recipes")
local MainGui = script.Parent
local MainFrame = MainGui:WaitForChild("Main")
local CraftingMaterialsFrame = MainFrame:WaitForChild("CraftingMaterials")
local Result = MainFrame:WaitForChild("Result")
local Template = script:WaitForChild("Template")
local itemData = {}
local recipeData = {}
local craftingData = {}
local CurrentButton
for _, itemModule in ipairs(Items:GetChildren()) do -- Loops through each module under the "Items" folder
local data = require(itemModule)
itemData[itemModule.Name] = data -- Storing each item in the "itemData" table
local newTemplate = Template:Clone() -- Cloning the template
newTemplate.Name = itemModule.Name
newTemplate.Image = data.Image
newTemplate.Parent = CraftingMaterialsFrame
newTemplate.MouseButton1Click:Connect(function() -- When this template is clicked
CraftingMaterialsFrame.Visible = false -- Hides the "CraftingMaterials" frame
CurrentButton.Image = itemData[itemModule.Name].Image -- Setting the image to the current item image
craftingData[tonumber(CurrentButton.Name)] = itemData[itemModule.Name].Tags -- Inserting the data for this item to the corresponding crafting table slot
CurrentButton = nil -- Resetting this variable
local craftItem = CheckRecipes() -- Checks all recipes
if craftItem then
Result.Image = itemData[craftItem].Image -- Sets the image of the frame named, "Result" to the item being crafted
else
Result.Image = ""
end
end)
end
for _, recipeModule in ipairs(Recipes:GetChildren()) do -- Loops through each module under the "Recipes" folder
recipeData[recipeModule.Name] = require(recipeModule) -- Storing each recipe in the "recipeData" table
end
for index = 1, 9 do
local Button = MainFrame:WaitForChild(tostring(index)) -- Hacky way to get each button slot in the crafting table
Button.MouseButton1Click:Connect(function()
CraftingMaterialsFrame.Visible = true -- Shows the materials
CurrentButton = Button -- Sets the current button being edited to the current button
end)
Button.MouseButton2Click:Connect(function()
Button.Image = ""
end)
end
Result.MouseButton1Click:Connect(function()
for index = 1, 9 do
MainFrame[tostring(index)].Image = "" -- Resetting the image
end
end)
Look! Now we can place items in the crafting area!:
Keep in mind that this is only visual, no logic is happening.
Phase 4: Constructing the Interpreter
The interpreter will be a local script that runs through the modules and figures out what different things can craft into.
Now that we’ve made the basis of our script, I will now introduce the logic of our system.
When an item is placed into the crafting table, we will store the data of the item that was placed in the crafting table in the corresponding slot. For example, if we put a Stick
in the 3rd slot of the crafting table, we will set the 3rd “index” of craftingData
to the data of the stick:
craftingData[3] = itemData["Stick"].Tags
-- ^ ^ ^
-- Crafting slot Item placed Item tags
Let’s add that logic to our code:
newTemplate.MouseButton1Click:Connect(function()
CraftingMaterialsFrame.Visible = false
CurrentButton.Image = itemData[itemModule.Name].Image
craftingData[tonumber(CurrentButton.Name)] = itemData[itemModule.Name].Tags -- Inserting the data for this item to the corresponding crafting table slot
-- ^ ^ ^
-- Crafting slot Item placed Item tags
CurrentButton = nil
end)
When we want the item to be removed from one of the crafting slots, we will remove the data from the corresponding slot:
craftingData[3] = nil
-- ^ ^
-- Crafting slot Set to nil
Let’s add that logic to our code:
Button.MouseButton2Click:Connect(function()
Button.Image = ""
craftingData[tonumber(Button.Name)] = nil -- When the user inputs 2nd mouse button on this button, delete the data corresponding to this button
-- ^ ^
-- Crafting slot Set to nil
end)
Let’s create a function that compares craftingData
to all of the different recipes:
local function CheckRecipes()
for _, recipes in pairs(recipeData) do
end
end
Let’s not forget that clicking the button named Result
will clear all data from the crafting table:
Result.MouseButton1Click:Connect(function()
for index = 1, 9 do
MainFrame[tostring(index)].Image = ""
end
Result.Image = ""
craftingData = {} -- Clearing the crafting table
end)
Let’s make a function called “CheckRecipes” that will check if the current data in craftingData
matches any of the recipes. If a recipe is met, it will set the image of “Result” to the recipe being crafted. Otherwise, it will make the image of “result” to nothing:
local function CheckRecipes()
for itemName, recipeTable in pairs(recipeData) do -- Loops through all the different items
for _, recipe in ipairs(recipeTable) do -- Loops through each induvidual recipe for each induvidual item
local canCraft = true -- Variable to check if something didn't equal
for slot = 1, 9 do -- Checks every slot from 1 - 9
if recipe[slot] and craftingData[slot] then -- If both the "craftingData" and the "recipe" have a value in the index, "slot"
if not table.find(craftingData[slot], recipe[slot]) then -- If "craftingData[slot]" has an object with the tag of "recipe[slot]"
canCraft = false -- Values don't match, so we set this variables to false
break
end
elseif recipe[slot] and not craftingData[slot] -- If "recipe" has a value but "craftingData" doesn't
or craftingData[slot] and not recipe[slot] then -- If "craftingData" has a value but "recipe" doesn't
canCraft = false -- Values don't match, so we set this variables to false
break
end
end
if canCraft then -- If all values are equal
Result.Image = itemData[itemName].Image -- Sets the image of the frame named, "Result" to the item being crafted
return -- Stops checking
else
Result.Image = ""
end
end
end
end
This function will run every time an item is added or removed from the crafting table. Let’s put the function where this happens:
newTemplate.MouseButton1Click:Connect(function()
CraftingMaterialsFrame.Visible = false
CurrentButton.Image = itemData[itemModule.Name].Image
craftingData[tonumber(CurrentButton.Name)] = itemData[itemModule.Name].Tags
CurrentButton = nil
-- In the code that sets the values of the crafting table
CheckRecipes() -- Checks all recipes
end)
Button.MouseButton2Click:Connect(function()
Button.Image = ""
craftingData[tonumber(Button.Name)] = nil
-- In the code that removes the values of the crafting table
CheckRecipes() -- Checks all recipes
end)
And would you look at that! Everything works like a charm!:
Completed place file: Crafting.rbxl (37.2 KB)
Questions You May Ask
How do I create more crafting recipes?
You can create more recipes by following the format at: Phase 1: Creating Individual Items and Data
How do I create more items?
You can create more items by following the format at: Phase 1: Creating Individual Items and Data
Is this exploit proof?
No, it’s not exploit proof. You would have to integrate that yourself.
If you spot any errors or have any other questions, don’t be afraid to ask! Thank you for reading!