Crafting System Error

OK so this one is completely off course, and I’m not sure if Brian knows about this or not, but I’ll ask anyway. A while ago, @bixarrio helped me out to create an awesome Crafting System (thanks @bixarrio :slight_smile: ). However, to expand on the system a little bit, I tried implementing a proper ‘HandleRaycast()’ system, which basically is supposed to open the Crafting UI when the player gets to the table. My first attempt was a sloppy one, but my second attempt, whilst it opens up the UI normally on arrival of the player when he arrives to the table, my game gives out a constant NRE when the UI is open, and basically the crafting system gets messy. Here is what my latest attempt to get this to work looks like:

  1. I created a new script, attached to the player, called ‘MoveToCraftingTable.cs’, as follows:
using RPG.Core;
using RPG.Movement;
using UnityEngine;

namespace RPG.Crafting {

    public class MoveToCraftingTable : MonoBehaviour, IAction
    {

        private CraftingTable target;
        private int acceptanceRadius;

        private void Start() {
            acceptanceRadius = 3;
        }

        public void Craft(CraftingTable target) {
            GetComponent<ActionSchedular>().StartAction(this);
            this.target = target;
        }

        public void Update() {

            if (target == null) return;

            if (!GetIsInRange(target.transform)) {
                GetComponent<Mover>().MoveTo(target.transform.position, 1.0f);
            }
            else {
                GetComponent<Mover>().Cancel();
                target.NotifyCrafting();
            }

        }

        public bool GetIsInRange(Transform target) {
            return Vector3.Distance(transform.position, target.transform.position) <= acceptanceRadius;
        }

        public void Cancel()
        {
            target = null;
            GetComponent<Mover>().Cancel();
        }
    }
}
  1. I went to ‘CraftingTable.cs’ and injected the new ‘IRaycastable’ Interface inheriting functions:

// Test (Delete if failed):
        private MoveToCraftingTable craftingTable;


public CursorType GetCursorType()
        {
            // Return the 'crafting' cursor type
            return CursorType.Crafting;
        }

public bool HandleRaycast(PlayerController callingController) {

            if (Input.GetMouseButtonDown(0)) {
                craftingTable.Craft(this);
            }
            return true;

        }

and this function as well (which is relevant to the NRE):

bool ICraftingTable.CallerIsInRange() {

            // NOT THE SAME as 'distanceToCraftingTable' in 'HandleRaycast', because this one checks for 'acceptanceRadius' as well
            return Vector3.Distance(transform.position, callerTransform.position) <= maxDistanceToCloseCraftingUI;

        }

// and my NotifyCrafting() function:

public void NotifyCrafting() {

            // This function is what is called to open the Crafting UI up

            // Let the CraftingMediator know that we are being interacted with
            var craftingMediator = CraftingMediator.GetCraftingMediator();
            craftingMediator.NotifyInteraction(this);

        }

and here is the ‘ICraftingTable.cs’ Interface components:

using System;

namespace RPG.Crafting
{
    public enum CraftingState
    {
        Idle,
        Crafting
    }

    public interface ICraftingTable
    {
        event Action<ICraftingTable> OnInteract;
        event Action CraftingStarted;
        event Action<float> CraftingProgress;
        event Action CraftingCompleted;
        event Action CraftingCancelled;

        Recipe CurrentRecipe { get; }
        CraftedItemSlot CraftedOutput { get; }
        CraftingState CurrentState { get; }
        float CraftingPercentage { get; }

        Recipe[] GetRecipesList();
        bool CanCraftRecipe(Recipe recipe);
        void CraftRecipe(Recipe recipe);
        void CancelCrafting();
        void UpdateState();
        bool CallerIsInRange(); // additional function by Bahaa, so that our game closes the Crafting UI if we suddenly walk away
    }
}

And finally, this is the NRE I keep getting:

NullReferenceException: Object reference not set to an instance of an object
RPG.Crafting.CraftingTable.RPG.Crafting.ICraftingTable.CallerIsInRange () (at Assets/Asset Packs/bixarrio/RPG Crafting System/Scripts/CraftingTable.cs:151)
RPG.Crafting.CraftingSystem.Update () (at Assets/Asset Packs/bixarrio/RPG Crafting System/Scripts/CraftingSystem.cs:96)

Which only occurs when the player arrives to the table

How do I fix this?

First of all, great job on implementing a Moveable class implemented.

The error occurs to originate in CraftingSystem.Update() which is calling the CallerIsInRange()… The CraftingTable may not have a callerTransform. This should be checked either before calling the method (which may not work from CraftingSystem or within CallerIsInRange (test for null first and return false if null, then return the Vector calculation.

OK so I did some modifications in ‘CallerIsInRange()’, and got the whole ‘open when you arrive’ thing to work properly, and here is the new ‘CallerIsInRange()’ code:

bool ICraftingTable.CallerIsInRange() {

            callerTransform = Inventory.GetPlayerInventory().GetComponent<PlayerController>().transform;
            if (callerTransform == null) return false;
            // NOT THE SAME as 'distanceToCraftingTable' in 'HandleRaycast', because this one checks for 'acceptanceRadius' as well
            return Vector3.Distance(transform.position, callerTransform.position) <= maxDistanceToCloseCraftingUI;

        }

but there’s a brand new problem. When it opens, THE CRAFTING UI is literally UNINTERACTABLE WITH. In other words, the flashes that occur when you’re short on items doesn’t work, the crafting button isn’t working… the whole thing basically won’t work. Where did I go wrong?

If it helps in anyway, it used to work perfectly fine with my old ‘HandleRaycast()’ method:

public bool HandleRaycast(PlayerController callingController)
        {
            // If there are no discovered recipes, return false
            if (recipes == null || recipes.Length == 0)
            {
                return false;
            }

            // If the player presseed the left mouse button, open the UI
            if (Input.GetMouseButtonDown(0))
            {

                // distance between player and table, which, if the player walks away from, the Crafting UI automatically shuts down
                callerTransform = callingController.transform;

                float distanceToCraftingTable = Vector3.Distance(transform.position, callingController.transform.position);

                if (distanceToCraftingTable > acceptanceRadius) {

                    Debug.Log("Moving towards the Crafting Table");
                    callingController.GetComponent<Mover>().MoveTo(transform.position, 1.0f);
                    isMovingTowardsCraftingTable = true;

                    if (isMovingTowardsCraftingTable && distanceToCraftingTable <= acceptanceRadius) {

                        Debug.Log("Opening Crafting UI...");
                        NotifyCrafting();
                        isMovingTowardsCraftingTable = false;

                    }

                    return true;

                }

                else if (distanceToCraftingTable <= acceptanceRadius) {

                    Debug.Log("Opening Crafting UI...");
                    NotifyCrafting();
                    isMovingTowardsCraftingTable = false;
                    return true;

                }

            }

            if (isMovingTowardsCraftingTable && Vector3.Distance(transform.position, callingController.transform.position) <= acceptanceRadius) {

                NotifyCrafting();
                isMovingTowardsCraftingTable = false;
                return true;

            }

            return true;
        }

I’m still trying to figure out where I went wrong…

I think I have enough information so that when I get home, I’ll write a proper IHandleRaycast and modify MovetoCraftingTable accordingly. It looks like you might have gotten something tangled up.

1 Like

Got it, thank you again for guiding me Brian :slight_smile:

OK so after CAREFULLY messing around with my code a little bit (guaranteed, the best solution (for me at least) to figure something out :stuck_out_tongue_winking_eye: ), I managed to make it work. I noticed that the crafting table got messy because… well… I’m calling the table FROM THE UPDATE FUNCTION. In other words, it’s getting called so many times, the game doesn’t even give the player the chance to interact with it

To combat this, after a bit of googling, I introduced a boolean called ‘isCalled’, which ensures the function is only called when ‘isCalled’ is set to true (and it’s set to false if the player is far from the table). All these changes were made in ‘MoveToCraftingTable.cs’

Long story short, it seems to be working fine now. Time to go back to the dual-handed weapons :smiley:

If anyone reads this down the line and needs help, here is the ‘MoveToCraftingTable.cs’ code:

using RPG.Core;
using RPG.Movement;
using UnityEngine;

namespace RPG.Crafting {

    public class MoveToCraftingTable : MonoBehaviour, IAction
    {

        private CraftingTable target;
        private int acceptanceRadius;

        private bool isCalled = true;

        private void Start() {
            acceptanceRadius = 3;
        }

        public void Craft(CraftingTable target) {
            this.target = target;
            GetComponent<ActionSchedular>().StartAction(this);
        }

        public void Update() {

            if (target == null) return;

            if (!GetIsInRange(target.transform)) {
                isCalled = false;
                GetComponent<Mover>().MoveTo(target.transform.position, 1.0f);
            }
            else {
                if (!isCalled) {
                GetComponent<Mover>().Cancel();
                target.NotifyCrafting();
                isCalled = true;
                }
            }

        }

        public bool GetIsInRange(Transform target) {
            return Vector3.Distance(transform.position, target.transform.position) <= acceptanceRadius;
        }

        public void Cancel()
        {
            target = null;
            GetComponent<Mover>().Cancel();
        }
    }
}

Anyday that we introduce a new system or solve a small issue, be it big or small, is always a happy day for me, and I’m more than happy you both are helping me out. Thanks guys :slight_smile:

The isCalled looks to me like it would cause the table to not open if you were already in range when you clicked on it…

This should be much simpler than you’ve made it. Sorry I didn’t get to it last night, I was exhausted after work and just answered course level questions. I should get to it tonight.

I’ll have to test proof that once more. No worries Brian, we all get tired :grinning_face_with_smiling_eyes:

I didn’t even know this was an issue though, mainly because I didn’t get to close the UI (the X button was overshadowed by the minimap), and just like you said, it failed… Thank you for pointing it out though, I’ll try fix it from my end as well :slight_smile:

OK so after 3 hours of thinking, this turned out to be an incredibly easy fix. Basically I did no changes in ‘MoveToCraftingTable.cs’, and thanks to you mentioning the ‘Raycast’ function before, I got a fun little idea (and it’s robust now):

All changes were done in ‘CraftingTable.cs’:

  1. Create a ‘PlayerIsInRange()’ function, in ‘CraftingTable.cs’:
        private bool PlayerIsInRange() {
            Inventory player = Inventory.GetPlayerInventory();
            return Vector3.Distance(transform.position, player.transform.position) <= acceptanceRadius;
        }
  1. Integrate that in ‘HandleRaycast()’, in an ‘if’ statement that checks if you’re close to the table or not. If you are, just open up the UI. If not, then call ‘MoveToCraftingTable.cs’, so now it looks like this:
public bool HandleRaycast(PlayerController callingController) {

            if (recipes == null || recipes.Length == 0) return false;

            if (Input.GetMouseButtonDown(0)) {
                
                if (PlayerIsInRange()) NotifyCrafting();
                else craftingTable.Craft(this);
            }
            return true;
        }

(I’ll probably fix these names otherwise these functions can get incredibly confusing down the line)

Now we can go back to Dual Hand weapons :stuck_out_tongue_winking_eye: (that’s where I’ll seriously need help)

Here’s my solution:
First, the MoveToCraftingTable

public class MoveToCraftingTable : MonoBehaviour, IAction
{
    [SerializeField] float acceptanceRadius=3;
    Mover mover;
    CraftingTable target;
    
    void Awake()
    {
        mover = GetComponent<Mover>();
    }
    void Update()
    {
         if(target==null) return;
         if(Vector3.Distance(target, transform.position) < acceptanceRadius)
         {
              mover.MoveTo(target.transform.position);
         } else
         {
               target.NotifyCrafting();
               target=null;
          }
    }
    
    public void StartCraftingAction(CraftingTable table)
    {
         GetComponent<ActionScheduler>().StartAction(this);
         target=table;
     }
 
     public void Cancel()
     {
           target = null;
     }
}

And in CraftingTable.cs

public bool HandleRaycast(PlayerController callingController)
{
     if(Input.GetMouseDown(0))
     {
          callingController.GetComponent<MoveToCraftingTable>().StartCraftingAction(this);
     }
     return true;
}

Hello Brian. I have went through your solution, and after fixing a few mistakes, it did not work as expected (when I press on the table, the player remains idle). It’s all good though, mine works fine for now :slight_smile: - we can move on to other topics xD

Privacy & Terms