Skip to content

Instantly share code, notes, and snippets.

@AdiRidA

AdiRidA/Craft.cs Secret

Last active November 28, 2023 10:51
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save AdiRidA/5529e2114b91526b0dae8fc8a333380b to your computer and use it in GitHub Desktop.
Save AdiRidA/5529e2114b91526b0dae8fc8a333380b to your computer and use it in GitHub Desktop.
RPG Crafting System Source Code
using UnityEngine;
using RPG.Inventories;
namespace RPG.Crafting
{
public class Craft : MonoBehaviour
{
public void CraftItem(Inventory inventory, InventoryItem inventoryItem, CraftingRecipe.Recipes recipe)
{
// Check if the player has the ingredients in the inventory.
if(HasIngredients(inventory, recipe))
{
// If true
// Rremove the items from the inventory.
RemoveItems(inventory, recipe);
// Add the recipe result item to the first empty slot in the inventory.
inventory.AddToFirstEmptySlot(inventoryItem, 1);
}
}
private void RemoveItems(Inventory inventory, CraftingRecipe.Recipes recipe)
{
// Iterate through all of the ingredients in the specified recipe in the parameter.
foreach (CraftingRecipe.Ingredients ingredient in recipe.ingredients)
{
// Check if the current ingredient iteration's item is stackable.
if (ingredient.item.IsStackable())
{
// If the item is stackable, get the slot index number the item is found in.
// Assign the slot index number in a variable.
int itemSlot = inventory.GetItemSlot(ingredient.item, ingredient.number);
// Remove the items from the player's inventory slot with the amount number.
inventory.RemoveFromSlot(itemSlot, ingredient.number);
}
else
{
// If the item is NOT stackable, loop through the current foreach loop iteration's ingredient amount number.
for (int i = 0; i < ingredient.number; i++)
{
// Get the slot index number that the item is found in.
// Because we know that the item is not stackable, we can simply say that the amount is 1.
int itemSlot = inventory.GetItemSlot(ingredient.item, 1);
// Remove the items from the player's inventory slot.
inventory.RemoveFromSlot(itemSlot, 1);
}
}
}
}
private bool HasIngredients(Inventory inventory, CraftingRecipe.Recipes recipe)
{
// Create boolean to store result.
bool hasItem = false;
// Iterate through all of the ingredients in the specified recipe in the parameter.
foreach(CraftingRecipe.Ingredients ingredient in recipe.ingredients)
{
// Check if the current ingredient iteration's item is stackable.
if(ingredient.item.IsStackable())
{
// If the item is stackable, get the inventory slot index number that the stack is in.
// We check if the returned value equals to or is greater than 0, because GetItemSlot returns -1 by default if no slot is found.
// Assign the if statement value to the boolean variable.
hasItem = inventory.GetItemSlot(ingredient.item, ingredient.number) >= 0;
}
else
{
// If the item is NOT Stackable, check if the required item is in the player's inventory.
// Assign the if statement value to the boolean variable.
hasItem = inventory.HasItem(ingredient.item, ingredient.number);
}
// If the assigned value is false, the method will return false.
if(!hasItem) return false;
}
// The method will return true by default.
return true;
}
}
}
using UnityEngine;
using RPG.Inventories;
namespace RPG.Crafting
{
[CreateAssetMenu(menuName = "Crafting/Crafting Recipe")]
public class CraftingRecipe : ScriptableObject
{
[SerializeField] Recipes[] recipes;
[System.Serializable]
public class Recipes
{
public InventoryItem item;
public Ingredients[] ingredients;
}
[System.Serializable]
public class Ingredients
{
public InventoryItem item;
public int number;
}
public Recipes[] GetCraftingRecipes()
{
return recipes;
}
}
}
using UnityEngine;
using RPG.UI.Inventories;
using RPG.Inventories;
namespace RPG.UI.Crafting
{
public class CraftingSlotUI : MonoBehaviour, IItemHolder
{
[SerializeField] InventoryItemIcon icon = null;
InventoryItem item;
public void Setup(InventoryItem item, int number)
{
// Store the item parameter value in the local item variable.
this.item = item;
// Set both the item image and the amount number on the UI.
icon.SetItem(item, number);
}
public InventoryItem GetItem()
{
// Return the set value in the item variable for the tooltip system
return item;
}
}
}
using UnityEngine;
using UnityEngine.UI;
using RPG.Crafting;
using RPG.Inventories;
namespace RPG.UI.Crafting
{
public class CraftingUI : MonoBehaviour
{
[SerializeField] GameObject recipePrefab = null;
[SerializeField] CraftingSlotUI itemSlot = null;
[SerializeField] GameObject recipeArrow = null;
[SerializeField] Button craftButton = null;
CraftingRecipe craftingRecipe;
Inventory inventory;
private void Awake()
{
//Get the player inventory component using the GetPlayerInventory method in the Inventory class.
inventory = Inventory.GetPlayerInventory();
}
public void SetupRecipes(CraftingRecipe recipe)
{
// Assign the parameter value (recipe) to the cached reference recipe (craftingRecipe).
craftingRecipe = recipe;
// Call the Redraw method which instantiates and sets up the UI elements.
Redraw();
}
private void Redraw()
{
// Destroy all of the child elements in the current gameobject's transform.
DestroyChild(transform);
// Iterate through all of the crafting recipes.
for (int i = 0; i < craftingRecipe.GetCraftingRecipes().Length; i++)
{
// For each crafting recipe:
// Create a recipe holder gameobject in under the current transform.
var recipeHolder = Instantiate(recipePrefab, transform);
// Destroy all of the child elements in the recipe holder gameobject.
DestroyChild(recipeHolder.transform);
// Assign the current recipe iteration to a new variable.
var recipe = craftingRecipe.GetCraftingRecipes()[i];
// Create and setup recipe ingredient gameobject elements under the recipe holder gameobject transform.
CreateRecipeIngredients(recipe, recipeHolder.transform);
// Create and setup recipe objects. Arrow image, recipe result item and craft button.
CreateRecipeObjects(craftingRecipe.GetCraftingRecipes()[i].item, recipeHolder.transform, recipe);
}
}
private void CreateRecipeIngredients(CraftingRecipe.Recipes recipe, Transform recipeHolder)
{
// Store recipe ingredients length in a variable.
int ingredientsSize = recipe.ingredients.Length;
// Loop through all of the ingredients in the recipe.
for (int ingredient = 0; ingredient < ingredientsSize; ingredient++)
{
// Create the itemSlot prefab and make it a child under the recipeHolder transform.
var ingredientItem = Instantiate(itemSlot, recipeHolder);
// Set up the item’s (ingredientItem) icon and number amount.
ingredientItem.Setup(recipe.ingredients[ingredient].item, recipe.ingredients[ingredient].number);
}
}
private void CreateRecipeObjects(InventoryItem inventoryItem, Transform recipeHolder, CraftingRecipe.Recipes recipe)
{
// Create the arrow image UI element and make it a child under the recipeHolder transform.
var arrow = Instantiate(recipeArrow, recipeHolder);
// Instantiate the itemSlot prefab and make it a child under the recipeHolder transform.
var item = Instantiate(itemSlot, recipeHolder);
// Set up the item’s (item) icon and number amount.
item.Setup(inventoryItem, 1);
// Create the crafting button UI element and make it a child under the recipeHolder transform.
var button = Instantiate(craftButton, recipeHolder);
// Get the Craft script component from the button gameobject and store it in a variable.
var craft = button.GetComponent<Craft>();
// Add a listener to the button onClick event using a lambda expression.
button.onClick.AddListener(() => craft.CraftItem(inventory, inventoryItem, recipe));
}
private void DestroyChild(Transform transform)
{
// Iterate through all child transforms of the parameter specified transform.
foreach (Transform child in transform)
{
// Remove each iterated transform.
Destroy(child.gameObject);
}
}
}
}
using UnityEngine;
using RPG.Crafting;
using RPG.Controls;
using RPG.Movement;
namespace RPG.UI.Crafting
{
public class ShowCraftingUI : MonoBehaviour, IRaycastable
{
[SerializeField] CraftingRecipe craftingRecipe = null;
[SerializeField] CraftingUI craftingItems = null;
[SerializeField] GameObject craftingUI = null;
[SerializeField] float minimumCraftingDistance = 2.5f;
PlayerController playerController;
CharacterMovement characterMovement;
GameObject[] craftingTables;
private void Awake()
{
// Find the player gameobject using the tag "Player", and get its PlayerController component.
playerController = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerController>();
// Find the CharacterMovement component from the playerController.
characterMovement = playerController.GetComponent<CharacterMovement>();
// Find all game objects with the tag "CraftingTable".
craftingTables = GameObject.FindGameObjectsWithTag("CraftingTable");
}
private void Start()
{
// Disable the crafting UI gameobject.
craftingUI.SetActive(false);
}
private bool IsWithinDistance(PlayerController playerController)
{
// Iterate through the craftingTables GameObjects array.
foreach(GameObject craftingTable in craftingTables)
{
// Using Vector3.Distance check the distance between the player and the current iteration.
// We use the cached PlayerController component to get the position of the gameobject.
// Return true if yes.
if(Vector3.Distance(playerController.transform.position, craftingTable.transform.position) < craftingRange) return true;
}
// Return false by default.
return false;
}
private void Update()
{
// Check if IsWithinDistance returns false.
if(!IsWithinDistance(playerController) && craftingUI.activeSelf)
{
// Disable the crafting UI gameobject.
craftingUI.SetActive(false);
}
}
public CursorType GetCursorType()
{
// Return the desired CursorType enum member.
return CursorType.Crafting;
}
public bool HandleRaycast(PlayerController callingController)
{
// Check if the player clicked the left mouse button.
if(Input.GetMouseButtonDown(0))
{
// If the left mouse button was clicked, check if the player is not within the minimum distance to the crafting table.
if(!IsWithinDistance(callingController))
{
// If outside minimum distance:
// Move player character to the crafting table position.
characterMovement.MoveTo(transform.position, 1f);
}
else
{
// If within minimum distance:
// Cancel character movement using the navmesh isStopped property.
characterMovement.Cancel();
// Setup the recipes in the crafting UI.
// The SetupRecipes function simply assigns craftingRecipe to the local variable in CraftingUI component (craftingItems).
// We don’t do this in Awake or Start for example, because there might be more than one crafting tables that offer different recipes.
craftingItems.SetupRecipes(craftingRecipe);
// Enable the crafting UI gameobject.
craftingUI.SetActive(!craftingUI.activeSelf);
}
}
return true;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment