Merging the Third Person Controller Course with the RPG Course

beat you to it :laughing:

void HandleAttackButtonPressed() 
        {

            // This function is a generic option, which gets the player to detect what he sees around him, and based on that,
            // he can interact with the environment automatically, based on what he finds

            // Uncomment the statement below if you want the player to be able to fight out of combat mode:
            /* if (stateMachine.Targeter.HasTargets) 
            {
                stateMachine.SwitchState(new PlayerAttackingState(stateMachine, 0));
                return;
            } */

            if (stateMachine.PickupFinder.HasTargets)
            {
                InputReader_HandlePickupEvent();
                if (stateMachine.PickupFinder.CurrentTarget) return;
            }

            if (stateMachine.ConversantFinder.HasTargets)
            {
                InputReader_HandleDialogueEvent();
                if (stateMachine.ConversantFinder.CurrentTarget) return;
            }

            if (stateMachine.ShopFinder.HasTargets) 
            {
                InputReader_HandleShopEvent();
                if (stateMachine.ShopFinder.CurrentTarget) return;
            }

            // Uncomment the statement below if you want the player to be able to fight out of combat mode:
            // stateMachine.SwitchState(new PlayerAttackingState(stateMachine, 0));

        }

as for the action map, yes this one is an easy change I suppose… just create a new button in the action map itself, add it to your ‘InputReader.cs’ and then change the syntax in the function I just wrote above

my worry is, which parts of the code do we flush out to polish that system? (I’ll give this some time, once I understand why in the world is the EBS Saving system failing right now…?!)

OK Soo… because I don’t have much to do at work today, I decided to try the Crafting Table Range Finder challenge, and whilst it did work to an extent, because the UI does open up (although I did not make it a child of WindowController yet… and because I don’t know which script in my crafting system is responsible for that), my state machine for that is incomplete. The problem I currently have is that, whilst the UI opens when the assigned Input Action button is clicked, the player no longer responds to movement buttons even after I close the UI, so he’s unable of walking away from the table, or pretty much go anywhere…

SOO… I need a bit of help completing this:

using RPG.Crafting;

namespace RPG.States.Player
{
    public class PlayerCraftingState : PlayerBaseState
    {
        private CraftingTable target;

        public PlayerCraftingState(PlayerStateMachine stateMachine, CraftingTable craftingTable) : base(stateMachine)
        {
            target = craftingTable;
        }

        public override void Enter()
        {
            target.NotifyCrafting();
        }

        public override void Tick(float deltaTime) {}

        public override void Exit()
        {
            
        }
    }
}

mainly because everytime I look at Dialogues and Shop state machines, I get baffled on what on earth I want to do next. Might also be helpful to tag @bixarrio here, if he’s keen on joining us :slight_smile:

After that will be the bank, and interactables will be done for good (well, at least before I start tuning it in my own ways)

I haven’t touched the crafting system in a while and looking at my repo I don’t know where target.NotifyCrafting() comes from, but you’ll probably just want to bind to a ‘CraftingTableClosed’ event (or something like that) so that you can get out of this state, because you are not. It would then just be something like

using RPG.Crafting;

namespace RPG.States.Player
{
    public class PlayerCraftingState : PlayerBaseState
    {
        private CraftingTable target;

        public PlayerCraftingState(PlayerStateMachine stateMachine, CraftingTable craftingTable) : base(stateMachine)
        {
            target = craftingTable;
        }

        public override void Enter()
        {
            target.CraftingTableClosed += OnCraftingTableClosed;
            target.NotifyCrafting();
        }

        public override void Tick(float deltaTime) {}

        public override void Exit()
        {
            target.CraftingTableClosed -= OnCraftingTableClosed;
        }
        
        private void OnCraftingTableClosed()
        {
            ReturnToLocomotion(); // or whatever you have to return to where you were before crafting
        }
    }
}

‘target.NotifyCrafting()’ is not something you introduced in your system. It’s just a function I created out of 2 lines, to be able to open the CraftingUI, under ‘CraftingTable.cs’:

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);

        }

Anyway, I’ll try the State Machine and keep you updated on what to do next in a few minutes :slight_smile:

Ah, I see.

The crafting table will need the new event and when the UI closes it will have to fire the event. The problem is that other classes can’t fire events from classes that are not them, so the UI will have to call a method on crafting table, and then crafting table will have to fire the event.

// Crafting Table
public event Action CraftingTableClosed;

public void CloseCraftingTable()
{
    CraftingTableClosed?.Invoke();
}

// Crafting Table UI
public void Close() // this is not new, use the function that closes the crafting table UI
{
    // ... all the existing 'close' code, then
    currentCraftingTable.CloseCraftingTable();
}

The rest is the code in the state that subscribes to the event and waits for it to fire before changing back to the state it came from


Edit
Looked at my repo. The closing happens in the CraftingSystem. So, CraftingSystem.CloseCrafting() is where you would tell the crafting table to fire the event.

// Bound to the 'Close Button'
public void CloseCrafting()
{
    if (!craftingWindow.gameObject.activeSelf)
    {
        // If the crafting UI is already closed, there's no need to clean it up
        return;
    }

// ----- THIS IS NEW -----
    // Let the crafting table know it's being closed
    currentCraftingTable.CloseCraftingTable();
// -----------------------

    // Unsubscribe to all the events
    currentCraftingTable.CraftingStarted -= OnCraftingStarted;
    currentCraftingTable.CraftingProgress -= OnCraftingProgress;
    currentCraftingTable.CraftingCompleted -= OnCraftingCompleted;
    currentCraftingTable.CraftingCancelled -= OnCraftingCancelled;
    recipeOutput.ItemRemoved -= OnOutputRemoved;
    
    // Cleanup the UI
    Cleanup();
    // Close the Crafting UI
    craftingWindow.SetActive(false);
    // remove the reference to the crafting table
    currentCraftingTable = default;
}

That location can change to anywhere in this function as long as it’s after the first if and before we set the currentCraftingTable = default;

for the ‘Crafting Table UI’ script, are we talking about this one?

using GameDevTV.UI;
using UnityEngine;
using UnityEngine.Events;

namespace RPG.Crafting
{
    public class ShowHideCraftingUI : MonoBehaviour
    {
        // A unity event to allow us to hook up handlers in the inspector
        [SerializeField] UnityEvent onModalActive;

        private void Start()
        {
            // Subscribe to the OnModalActive event
            ShowHideUI.OnModalActive += OnModalActive;
        }

        private void OnDestroy()
        {
            // Unsubscribe from the OnModalActive event
            ShowHideUI.OnModalActive -= OnModalActive;
        }

        public void OnModalActive()
        {
            // Handle the event
            onModalActive.Invoke();
        }
    }
}

I can’t find a ‘CraftingUI.cs’ or ‘CraftingTableUI.cs’ script

No, look at my edit

yup, found the script, but this got us into a new problem:

‘currentCraftingTable’ is of type ICraftingTable not just CraftingTable… do we create a new variable instead, or do we cast it as ‘CraftingTable’ type…? (Idek if that’s possible, just a wild guess)

No, you add the method to the interface, too. It’s now part of the contract

public interface ICraftingTable
{
    // ... existing stuff
    void CraftingTableClosed();
}

It’s totally possible because CraftingTable is an ICraftingTable, but we don’t want to have any reference to CraftingTable here, so we won’t be casting anything.

Yup, added it too, but it still didn’t solve the problem that my player no longer responds to inputs once you open the UI, and yes the button is placed in the proper place in the action map. Here is the StateMachine again, which I suspect is the main problem:

using RPG.Crafting;

namespace RPG.States.Player
{
    public class PlayerCraftingState : PlayerBaseState
    {
        private CraftingTable target;

        public PlayerCraftingState(PlayerStateMachine stateMachine, CraftingTable craftingTable) : base(stateMachine)
        {
            target = craftingTable;
        }

        public override void Enter()
        {
            target.CraftingTableClosed += OnCraftingTableClosed;
            target.NotifyCrafting();
        }

        public override void Tick(float deltaTime) {}

        public override void Exit()
        {
            target.CraftingTableClosed -= OnCraftingTableClosed;
        }

        private void OnCraftingTableClosed() 
        {
            stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
        }
    }
}

Your player won’t respond to input while the table is open. There is nothing in the crafting state handling input. Generally, movement doesn’t happen while someone is in a crafting menu. I remember now that you want to walk away from the table to close it, so you will have to implement all the code from the movement state in here as well. Or crafting shouldn’t actually be a state

even when it’s closed AFTER he reaches to it and opens it, he still doesn’t respond to input… like, it just permanently destroys the connection between the player and the controls :sweat_smile:

OK not really, for this case I want the player to be frozen in state until he clicks the ‘x’ button to close the UI, and only then is he allowed to move away from the Crafting Table, so I want to reactivate the input only after he walks away from the table (both the player input and the cinemachine camera rotation in this case… completely pausing the game at that point is not my ideal solution tbh)

If the input doesn’t work after the table closes it’s possible that the event didn’t fire. Put a Debug.Log in OnCraftingTableClosed to confirm. If it doesn’t fire it means nothing is telling it to.

This is much easier to work with. Because the ‘Crafting’ state doesn’t handle input, the player won’t be able to move. Once the table closes and the event is fired, the player will return to the freelook state (like in your code) and input will get handled again. You just have to make sure that event is actually fired

OK so… when you hit the ‘L’ button, you can interact with the UI, but once you hit that button (the ‘L’ button, my temporarily assigned key for interacting with the Crafting Table), you are permanently disconnected from all your controls

and the debug doesn’t get called for some reason

Frankly speaking, unlike what Brian did, where he pauses the game fully if any UI opens, I don’t like this approach. Personally, I want my old approach, where you can still have the game work around you as usual if you got UI open, and maybe add in the cinemachine rotation freezer until you’re done with some of the UI elements

I believe the pause should only work for the pause menu, and that’s it :slight_smile: (but that’s for another day. For now, I just want the construction saving system to work, and the Crafting UI)

You are not ‘disconnected’, the state just doesn’t handle input.

And there’s the problem. You need to find out why.

It’s probably because you are using the ShowHideUI but, like we did in your previous version, you also need to call CraftingSystem.CloseCrafting(). The UnityEvents made it easy

Are we talking about this function, in ‘ShowHideUI.cs’?

public void DisableCraftingTable() {

            craftingTableUI.SetActive(false);
            Debug.Log("Crafting Table deactivated");

        }

P.S: I made a copy of ‘ShowHideUI.cs’, for the crafting table, called it ‘ShowHideCraftingUI.cs’, as follows:

using GameDevTV.UI;
using UnityEngine;
using UnityEngine.Events;

namespace RPG.Crafting
{
    public class ShowHideCraftingUI : MonoBehaviour
    {
        // A unity event to allow us to hook up handlers in the inspector
        [SerializeField] UnityEvent onModalActive;

        private void Start()
        {
            // Subscribe to the OnModalActive event
            ShowHideUI.OnModalActive += OnModalActive;
        }

        private void OnDestroy()
        {
            // Unsubscribe from the OnModalActive event
            ShowHideUI.OnModalActive -= OnModalActive;
        }

        public void OnModalActive()
        {
            // Handle the event
            onModalActive.Invoke();
        }
    }
}

Probably. You pasted a ShowHideCraftingUI script above (here) that has a UnityEvent. Just add CraftingSystem.CloseCrafting() to the list in the inspector. Although the crafting system also closes the window.

It was there the entire time… :sweat_smile: - and the issue persisted through it all

If it helps, here is the ‘CraftingSystem.CloseCrafting()’ function:

// Bound to the 'Close Button'
        public void CloseCrafting()
        {
            if (!craftingWindow.gameObject.activeSelf)
            {
                // If the crafting UI is already closed, there's no need to clean it up
                return;
            }

            // let the crafting table know it's being closed:
            currentCraftingTable.CloseCraftingTable();

            // Unsubscribe to all the events
            currentCraftingTable.CraftingStarted -= OnCraftingStarted;
            currentCraftingTable.CraftingProgress -= OnCraftingProgress;
            currentCraftingTable.CraftingCompleted -= OnCraftingCompleted;
            currentCraftingTable.CraftingFailed -= OnCraftingFailed;
            currentCraftingTable.CraftingCancelled -= OnCraftingCancelled;
            recipeOutput.ItemRemoved -= OnOutputRemoved;
            
            // Cleanup the UI
            Cleanup();
            // Close the Crafting UI
            craftingWindow.SetActive(false);
            // remove the reference to the crafting table
            currentCraftingTable = default;
        }

Privacy & Terms