Hi, I’m trying to make a crafting system.
However, I wrote a short script but I don’t know if it will continue and it confuses me a bit:
public class CraftingItem : MonoBehaviour
{
public int RequiredItems;
public SCItem[] Item;
private GameObject player;
private GameObject playerAction;
public GameObject FinishItem;
public void Start(){
player = GameObject.FindWithTag("Player");
}
public void Craft()
{
//drop item stuff
Instantiate(FinishItem, transform.position, transform.rotation);
}
}
What I want is something like craft if we have all items when we press the button like this one from the photo i.e.: If we get all items we can craft the axe or other things.
Help with completing the code.
Here is an example of how you can go about creating a crafting system.
-
create a recipe asset somewhere in the project window
-
create a go with these components:
-
call craftingBench.StartCrafting()
from somewhere (ui button, for example)
preview
If you want to Instantiate
something when crafting is complete, rather just increasing a number in an inventory - change the void CreateItem ()
method.
CraftingBenchComponent.cs
using System.Collections;
using UnityEngine;
public class CraftingBenchComponent : MonoBehaviour
{
public InventoryComponent inventory;
public CraftingRecipeAsset recipe;
Coroutine _craftingInProgress;
public void StartCrafting ()
{
if( DoIHaveAllTheIngredients()==true )
{
if( _craftingInProgress!=null ) StopCoroutine( _craftingInProgress );// prevent multiple crafting routines
_craftingInProgress = StartCoroutine( CraftingRoutine() );
}
else Debug.Log("can't craft - some ingredients are missing");
}
IEnumerator CraftingRoutine ()
{
Debug.Log($"{recipe.name} crafting started...");
float batchTime = 0;
while( batchTime<recipe.batchDuration )
{
yield return null;
batchTime += Time.deltaTime;
}
ConsumeIngredients();
CreateItem();
Debug.Log($"\t... {recipe.name} crafting completed!");
}
bool DoIHaveAllTheIngredients ()
{
for( int i=0 ; i<recipe.ingredients.Length ; i++ )
{
var ingredient = recipe.ingredients[i];
bool passed = false;
foreach( var item in inventory.content )
{
if( item.type==ingredient.type && item.quantity>=ingredient.quantity )
{
passed = true;
break;
}
}
if( !passed ) return false;
}
return true;
}
void ConsumeIngredients ()
{
for( int i=0 ; i<recipe.ingredients.Length ; i++ )
{
var ingredient = recipe.ingredients[i];
for( int k=0 ; k<inventory.content.Count ; k++ )
{
var item = inventory.content[k];// pull struct copy from array
if( item.type==ingredient.type )
{
item.quantity -= ingredient.quantity;
inventory.content[k] = item;// push struct copy on array back again
}
}
}
}
void CreateItem ()
{
bool itemCreated = false;
var items = inventory.content;
for( int i=0 ; i<items.Count ; i++ )
{
var item = items[i];// pull struct copy from array
if( item.type==recipe.product )
{
item.quantity += recipe.batchProducts;
items[i] = item;// push struct copy on array back again
itemCreated = true;
break;
}
}
if( !itemCreated )
{
items.Add( new InventoryItem{
type = recipe.product ,
quantity = recipe.batchProducts
} );
itemCreated = true;
}
}
}
CraftingRecipeAsset.cs
using UnityEngine;
[CreateAssetMenu( menuName="Game/Recipe Asset" , fileName="crafting recipe 0" , order=1 )]
public class CraftingRecipeAsset : ScriptableObject
{
public ESubstance product = ESubstance.Steel;
[Min(1)] public uint batchProducts = 1;
[Min(0.1f)] public float batchDuration = 1.5f;
public InventoryItem[] ingredients = new InventoryItem[]{
new InventoryItem{ type=ESubstance.Wood , quantity=20 } ,
new InventoryItem{ type=ESubstance.Iron , quantity=2 } ,
};
}
public enum ESubstance : uint
{
Wood ,
Iron ,
Steel
}
InventoryComponent.cs
using System.Collections.Generic;
using UnityEngine;
[DisallowMultipleComponent]
public class InventoryComponent : MonoBehaviour
{
public List<InventoryItem> content;
public void AddItems ( ESubstance type , uint quantity )
{
for( int i=0 ; i<content.Count ; i++ )
{
if( content[i].type==type )
{
var item = content[i];// pull struct copy from array
item.quantity += quantity;// modify it
content[i] = item;// push struct copy on array back again
return;
}
}
// item type not found so create a new entry:
content.Add( new InventoryItem{
type = type ,
quantity = quantity
} );
}
}
[System.Serializable]
public struct InventoryItem
{
public ESubstance type;
public uint quantity;
}
I like to follow the “KISS” method with everything I do. KISS stands for “Keep it simple, stupid”
Doing this will help in the long run. It will help with improving code legibility and upgrades.
There are several ways to do this but I will mention just one very very simple way.
Build a inventory script. ONLY KEEP YOUR INVENTORY FUNCTIONS AND VARIABLES HERE! The public variables should be something like…
public int stone = 0;
public int sticks = 0;
public int rope = 0;
....
...
..
When crafting, have your script check your inventory to see if it currently has the items needed.
For organization purposes, I recommend a switch statement.
For example…
public List thingsToCraft = new();
void CraftSomething(string thingToCraft) {
bool craftable = false;
GameObject spawnableObject;
switch (thingToCraft) {
case "axe":
if(
inventory.stone >= 1 &
inventory.rope >= 2 &
inventory.stick >= 1
) {
craftable = true;
spawnableObject = thingsToCraft[0];
inventory.stone -= 1;
inventory.rope -= 2;
inventory.stick -= 1;
}
break;
case "sword":
if(
inventory.stone >= 3 &
inventory.rope >= 1 &
inventory.stone >= 1
) {
craftable = true;
spawnableObject = thingsToCraft[1];
inventory.stone -= 3;
inventory.rope -= 1;
inventory.stone -= 1;
}
break;
}
if(craftable) {
Instantiate(spawnableObject, transformWhereYouWantIt, Quaternion.identity);
}
}
There are definitely some cleaner ways of doing this, however, this is a very straight-forward, simple execution method.
If it were me, Id consider something a little lazier but this method should work for someone who is newer to the idea in general.
I recommend starting with something like this and improving upon it as you see fit.
Hello.
Sincerly, no ofense, are you starting scripting, right? MAybe you should focus on smaller things than a crafting system, but anyway…
Its really simple, only need a int
variable for all items types, so you have stored how many of that items player have.
When crafting just need to check if the player have the items and subtract them.
int Log;
int Rock;
if (Log>=3 && Rock>=3)
{
Log -= 3;
Rock -= 3;
Craft (Axe);
}
But of course you will need to do something more elaborate, to not need to make a finction for each craftable item…
Something like a boll function to check a generic items and look if have all ingredients…
And for the button, just look on google “Unity add function to a button” There are some different ways to do it, andits really easy.
But as I said, maybe you should go learn more basic stuff on youtube/tutorials, and practice with simpler things before make your own crafting system… BUT trying hard things is how you learn
soo GOOD LUCK!
PD: If that was not your question, please be more specific and explain what are you asking for
I wrote you one that doesnt have the performance issues (of doing multiple unecessary nested loops), leans on the type system a little more and is is a bit more adaptable.
Here is a repo, its a bit late but I can explain it tomorrow if you dont understand it.
Most of the files are just interfaces or models and most of the code doing work is in the CraftingTable and Inventory with the Recipes being stored in the RecipeBook (there is one in there for a sword) and and example of its use being bunged into the Player object. Where I have just instantiated everything you need to see it working and also shows how to add things to the inventory and how to get crafted items out of the crafting table.
@sacredgeometry I was checking out your Gitcode, and you didn’t implement the interface ICraftingSource
in Player.cs
(and since I already have my own Inventory, the CraftingTable.cs
is kinda causing me clashing issues… any way we can go around this?)
By the way, is there any chance you can have a look at my game and help me implement a crafting system? I can really use some help as well