Merging the Third Person Controller Course with the RPG Course

hey Brian, just a heads up, are you aware that the freelook camera miraculously sometimes, when it can’t ignore a layer, just goes ahead and… gets the player to teleport to another area, or sometimes the targeting camera will miss the target for some reason? Just trying to make sure I’m not the only one with this bug :sweat_smile: (because if I am, then I’ll probably fix it alone)

I’ve never experienced that…

Or that…

Perhaps a video demonstrating?

it’s a 50-50 chance kinda bug, but I’ll give trying to replicate it a shot (it’ll take a bit of time to be able to get it…)

hey Brian, quick update on your Shop Challenge. Whilst it does work in the end, but you did forget to mention the variables that need to be included in the ‘PlayerStateMachine.cs’. I’ll help you out with them here :slight_smile: :

// in 'PlayerStateMachine.cs'

        [field: SerializeField] public Shopper Shopper {get; private set;}
        [field: SerializeField] public ShopFinder ShopFinder {get; private set;}

// in OnValidate():

            if (Shopper == null) Shopper = GetComponent<Shopper>();

// in 'PlayerFreeLookState.Exit()':

            stateMachine.InputReader.ShopEvent -= InputReader_HandleShopEvent;

Other than that, it works quite well. Thank you for your hard work for us :slight_smile: (however, it doesn’t freeze in time. Speaking of which, can we change that to freeze the camera instead?)

You’re right that I didn’t explicitly type out the code for this, but it is mentioned…

I did, however, forget to mention making the ShopUI a WindowController.

1 Like

Same thing can be done for crafting, right? For resource gathering, I think I’ll keep the mouse functionality… Still thinking this through (at this point in time, I’m buying and playing valheim to get a taste of what players are used to :skull:)

Yes, by this time, you should have a good firm grasp of the pattern that is applied to any UI type

  • Input Action, tied to an Event
  • Finder where necessary
  • Changing state where necessary
  • Integrating window into the WindowController setup

Will give it a go and keep you updated if I run into anything :slight_smile:

Haven’t tried the Crafting UI attempt yet, neither the bank, but…

Quick question on the fly, it’s just something that came on top of my head… Instead of, you know, creating a special state for every UI, have you considered making a generic class that automatically identifies what the player is about to interact with? So for example if I press “B”, and the closest thing is… idk… a Shop, then open the shop? If I walk around a little more, and the closest thing is a Bank, open that up, maybe a crafting table…? If so, why didn’t we try this out? Is it even possible? Why or why not?

The idea is, one button unlocks it all for interacting with the game world

I know this is a generic question, just debating an idea here :slight_smile:

That’s actually sort of covered in the HandleAttackButtonPressed in the PlayerFreeLookState().

        void HandleAttackButtonPressed()
        {
            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;
            }
            stateMachine.SwitchState(new PlayerAttackingState(stateMachine, 0));
        }

ahh, the function I commented out because I didn’t want to enable my player attacking anything outside of combat mode… :sweat_smile:

Shouldn’t that function have a ShopFinder Target search condition as well? I don’t see it there :sweat_smile:

That’s an easy fix…

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

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

(lowkey thinking of actually implementing all interactions to a single button, which is not the mouse button, otherwise that’ll whaffle everyone :stuck_out_tongue_winking_eye: ), maybe the ‘E’ button or something (and ‘F’ for mounts, if I ever get Malbers on board), and then we can lock the cursor and actually make this game clean :slight_smile:

Now I see the potential out of your idea. Brilliant thinking, Brian! (as usual)

For real though, you may want to add the same handle condition for the shopper as well :slight_smile: - just saying that it’s missing, xD

Two things here…

  1. You can bind that method to any key event you want. You should have the information you need to do just that.
  2. So here’s your challenge: Add the same handle condition for the shopper as well. :stuck_out_tongue:

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

Privacy & Terms