Merging the Third Person Controller Course with the RPG Course

[Further Instructions on the Bank Functions]

A. in ‘PlayerBankingState.cs’, I made a few changes in ‘Exit()’:

    public override void Exit()
    {
        Debug.Log("Player Exited Banking State");
        target.CloseBank(stateMachine);
        target.OtherInventoryClosed -= OnOtherInventoryClosed;
    }

B. In ‘OtherInventory.cs’, I made the following changes:

        public void CloseBank(PlayerStateMachine player) 
        {
            player.GetComponent<BankAction>().Cancel();
        }

C. in ‘BankAction.cs’, I changed the ‘Cancel()’ function a bit:

    public void Cancel() 
    {
        target = null;
        ThirdPersonShowHideUI.CloseAllWindows();
    }

D. in ‘ThirdPersonShowHideUI.cs’, I added this function:

    public static void CloseAllWindows() 
    {
        foreach (ThirdPersonShowHideUI tpShowHideUI in FindObjectsOfType<ThirdPersonShowHideUI>()) 
        {
            tpShowHideUI.UIContainer.SetActive(false);
            if (tpShowHideUI.otherInventoryUI != null) 
            {
                tpShowHideUI.otherInventoryContainer.SetActive(false);
            }
        }
    }

[PROBLEMS]

  1. Something to do with the InputReader.cs, I believe…:
MissingComponentException while executing 'performed' callbacks of 'UI/Bank[/Keyboard/x]'
UnityEngine.InputSystem.LowLevel.NativeInputRuntime/<>c__DisplayClass7_0:<set_onUpdate>b__0 (UnityEngineInternal.Input.NativeInputUpdateType,UnityEngineInternal.Input.NativeInputEventBuffer*)
UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate (UnityEngineInternal.Input.NativeInputUpdateType,intptr)
  1. Another one, this one has something to do with ‘Mover.cs’ trying to access the NavMeshAgent I believe…:
MissingComponentException: There is no 'NavMeshAgent' attached to the "Player" game object, but a script is trying to access it.
You probably need to add a NavMeshAgent to the game object "Player". Or your script needs to check if the component is attached before using it.
RPG.Movement.Mover.Cancel () (at Assets/Project Backup/Scripts/Movement/Mover.cs:115)
RPG.ResourceManager.ResourceGatherer.Cancel () (at Assets/Project Backup/Scripts/ResourceManager/ResourceGatherer.cs:208)
RPG.Core.ActionSchedular.StartAction (RPG.Core.IAction action) (at Assets/Project Backup/Scripts/Core/ActionSchedular.cs:32)
RPG.Bank.BankAction.StartBankAction (UnityEngine.Transform bankTarget, System.Action callback) (at Assets/Project Backup/Scripts/Bank/BankAction.cs:30)
GameDevTV.Inventories.OtherInventory.OpenBank (RPG.States.Player.PlayerStateMachine player) (at Assets/GameDev.tv Assets/Scripts/Inventories/OtherInventory.cs:20)
PlayerBankingState.Enter () (at Assets/Project Backup/Scripts/State Machines/Player/PlayerBankingState.cs:18)
RPG.States.StateMachine.SwitchState (RPG.States.State newState) (at Assets/Project Backup/Scripts/State Machines/StateMachine.cs:13)
RPG.States.Player.PlayerFreeLookState.InputReader_HandleBankingEvent () (at Assets/Project Backup/Scripts/State Machines/Player/PlayerFreeLookState.cs:198)
RPG.InputReading.InputReader.OnBank (UnityEngine.InputSystem.InputAction+CallbackContext context) (at Assets/Project Backup/Scripts/Input Controls/InputReader.cs:197)
UnityEngine.InputSystem.Utilities.DelegateHelpers.InvokeCallbacksSafe[TValue] (UnityEngine.InputSystem.Utilities.CallbackArray`1[System.Action`1[TValue]]& callbacks, TValue argument, System.String callbackName, System.Object context) (at Library/PackageCache/com.unity.inputsystem@1.5.1/InputSystem/Utilities/DelegateHelpers.cs:46)
UnityEngine.InputSystem.LowLevel.<>c__DisplayClass7_0:<set_onUpdate>b__0(NativeInputUpdateType, NativeInputEventBuffer*)
UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate(NativeInputUpdateType, IntPtr)

[IGNORE THIS ONE, IT DOESN’T HAPPEN THIS OFTEN ANYMORE]

and 3. When I use my Construction System, having this line in ‘PlayerFreeLookState.InputReader_HandleBankingEvent’ means I cannot properly exit my construction and re-activate my ‘PlayerStateMachine’. In other words, my player does not safely land from the other controller… What does this line have to do with anything?:

        private void InputReader_HandleBankingEvent()
        {
            stateMachine.SwitchState(new PlayerBankingState(stateMachine, stateMachine.BankFinder.GetNearestBank()));
        }

I don’t know why the last one no longer happens, but I could’ve sworn that it used to happen…


[THE MOST IMPORTANT PROBLEM OF ALL]

AND THE BIGGEST PROBLEM OF ALL: THE LONGER YOU USE THE BANK, THE SLOWER IT GETS… How do we optimize and speed up this process further, and more importantly, how do we delete any unwanted data once the bank is done and dusted, to free up space for future data that’ll need it? It’s a serious pain to deal with :sweat_smile: (any chance we can minimize the number of items being dealt with in ‘InventoryUI.Redraw’? Wouldn’t that help it out a bit further? I know we don’t need to destroy and re-instantiate each slot, so… how do we only do that for the slots being changed?)

There is a thread on here that covers the lag issue. One of our fellow students did an indepth analysis. I am at work, so I can’t look it up. You should be able to find it. It is worth the effort.

1 Like

Good day Ed, thanks. we’re not talking about the one I asked for a while ago, for transferring entire inventories by chance, are we? Is it this one?

No, not one of your topics. It was another poster. I don’t remember who.

Plays “Man on a Mission - Oh the Larceny”, as he goes to hunt that post down

Alright I’ll go try hunt it down

Sir Brian, if this comment is still here, I still need help :smiley:

Edit 1: Haven’t found the thread, but I found this - still need the optimization solution though

been searching for the last hour like a madman, I can’t find that thread… -_*

1 Like

[Inventory Redraw() Framerate hiccups with high slot count](Inventory Redraw() Framerate hiccups with high slot count]
Here is one. This one is for inventory lag.

Here is one that is for shops but has a lot of good stuff.

been searching for the last hour like a madman, I can’t find that thread… -_*
[/quote]

Try adding a little sanity. It helps. :joy:

NAH, I’m either a sane person, or I finish the game :stuck_out_tongue:

COME ON, WE’VE BEEN THROUGH HELL AND BACK WITH THIS ONE, xD

This project broke me countless times, I almost gave up like a thousand times so far, and then Brian comes up with a solution and I’m back to the next bone-breaking issue… Brian, please, bank optimization whenever you feel better and it’s possible… You are our saviour :smiley:

Anyway, here is where I suspect where the real problem is, in ‘InventoryUI.cs’:

private void Redraw()
        {
            foreach (Transform child in transform)
            {
                Destroy(child.gameObject);
            }

            for (int i = 0; i < selectedInventory.GetSize(); i++)
            {
                var itemUI = Instantiate(InventoryItemPrefab, transform);
                itemUI.Setup(selectedInventory, i);
            }
        }

I can’t properly think of a solution that would limit what gets destroyed and re-instantiated, but I know for a fact that I don’t need to destroy and re-instantiate EVERYTHING just to refresh the UI of a sender slot, and a receiver slot… that’s just a HUGE waste of resources that simply needs to get eliminated (and I can’t think of a solution as to how… :sweat_smile:)

In the meanwhile I shall go read Brian’s solution on that question @edc237 mentioned, MAYBE I can find something useful there

That is exactly what the first topic I linked above covers.

1 Like

I’m reading it through, believe me… I’m just seeing what can be extracted and placed into our script, since that topic is dedicated to solving the RowUI issue for shops… my issue is the UI for the inventory

Anyway, I’ll go grab something to eat for the time being (it’s 7PM, didn’t eat anything today), and then return to solve this issue

Look at the one above it. It is specifically about inventory.

1 Like

OK I’m a little confused… where? Who? @edc237 (AHH, NEVERMIND… SILLY ME… I HAVE COMPLETELY MISSED THAT COMMENT!)

(I’ll also go implement that solution tbh, that sounds like a solution to a nightmare waiting to happen)

OK so… 3 things to do:

  1. Fix the whole ‘X’ button working from anywhere and deactivating the player, when it’s not supposed to work unless he’s near the bank (Ahh… the dumb things that happen when you forget your ‘if’ statements)
  2. Wait for Brian’s solution for the bank UI
  3. Go through the article and fix the same issue that post writer has

This is the one that covers inventory.

yup, found it. Thank you @edc237 - now it’s time for me to choose which of the approaches mentioned in there to go with… I’ll probably go with the first though, let’s see what happens (I’ll backup my project first)

Edit 1: The first approach of this post failed… apparently it did not work AT ALL for me… In fact, removing ‘InventoryUI.Redraw()’ to replace it with the dictionary approach just ensured that I can’t pick anything off the ground, and more importantly, nothing goes into the inventory… So unless Brian hops on board, I’ll try analyze and understand what’s going on

If it helps though, here is my current ‘InventoryUI.cs’ script:

using UnityEngine;
using GameDevTV.Inventories;
using TMPro;
using UnityEngine.Events;
using System.Linq;
using System.Collections.Generic;

namespace GameDevTV.UI.Inventories
{
    /// <summary>
    /// To be placed on the root of the inventory UI. Handles spawning all the
    /// inventory slot prefabs.
    /// </summary>
    public class InventoryUI : MonoBehaviour
    {
        // CONFIG DATA
        [SerializeField] InventorySlotUI InventoryItemPrefab = null;

        // BANK System CONFIG DATA:
        [SerializeField] bool isPlayerInventory = true;
        [SerializeField] TextMeshProUGUI Title;

        // 3 bank <-> Inventory states: 
        // 'otherInventoryUI' (bank) is open, 
        // 'otherInventoryUI' (bank) is closed, 
        // YOU are the 'otherInventoryUI' (bank)

        public bool IsOtherInventory => !isPlayerInventory;
        public Inventory SelectedInventory => selectedInventory;

        [SerializeField] private UnityEvent EnabledEvent;
        [SerializeField] private UnityEvent DisabledEvent;

        // CACHE
        Inventory selectedInventory;

        // delete if failed
        // InventorySlotUI[] itemUIs;  // the items that get transferred, get refreshed

        // LIFECYCLE METHODS

        private void Awake() 
        {
            // if we are dealing with the player Inventory, set it up (just don't do it for the Players' bank)
            if (isPlayerInventory) {
            selectedInventory = Inventory.GetPlayerInventory();
            selectedInventory.inventoryUpdated += Redraw;
            }        
        }

        private void Start()
        {
            // Redraw the PLAYER INVENTORY, JUST NOT THE BANK!
            if (isPlayerInventory) Redraw();
        }

        // PRIVATE

        private void Redraw()
        {
            foreach (Transform child in transform)
            {
                Destroy(child.gameObject);
            }

            for (int i = 0; i < selectedInventory.GetSize(); i++)
            {
                var itemUI = Instantiate(InventoryItemPrefab, transform);
                itemUI.Setup(selectedInventory, i);
            }
        }

        // PUBLIC

        public bool Setup(GameObject user) {
            if (user.TryGetComponent(out selectedInventory)) {
                // if the object has an inventory (our bank in this case), set the Selected Inventory Up, 
                // Subscribe to the Redraw() changes, and then redraw the inventory (to boot it up for the first time):
                selectedInventory.inventoryUpdated += Redraw;
                Title.text = selectedInventory.name;
                Redraw();
                return true;
            }
            return false;
        }

        private void OnEnable()
        {
            EnabledEvent?.Invoke();
        }

        private void OnDisable()
        {
            DisabledEvent?.Invoke();
        }

        public void TransferAllInventory()
        {
            if (selectedInventory != null)
            {
                selectedInventory.TransferAllInventory(GetOtherInventory());
            }
        }

        private Inventory GetOtherInventory()
        {

            if (IsOtherInventory) return Inventory.GetPlayerInventory();

            // return the FIRST other Inventory it finds (i.e: The bank), or null:
            var otherInventoryUI = FindObjectsOfType<InventoryUI>().FirstOrDefault(ui => ui.IsOtherInventory);

            //Check to see if it's not null (should never happen), if it's Active, and if the otherInventory's inventory is valid.
            if (otherInventoryUI != null && otherInventoryUI.gameObject.activeSelf && otherInventoryUI.SelectedInventory != null)
            {
                Inventory otherInventory = otherInventoryUI.SelectedInventory;
                return otherInventory;
            }

            return null;
        
        }

    }

}

Once we actually get a solution for this, I’ll go also attempt to solve the lag spikes of the shop, from this thread… I just noticed I have these too

Edit 1: I can’t believe it’s been 12 hours, and I still can’t figure out an alternative solution to a stupidly easy algorithm… -_* (WHYYY :confused:)

Edit 2: A little bit of an update. I ended up using this Algorithm:

// ------------ TEST AREA: Rewriting the 'Redraw()' algorithm, so that the banking and/or inventory moving stuff around, works faster --------------

        int playerInventorySize;
        Dictionary<int, InventorySlotUI> inventorySlotCache;

        private void Redraw() 
        {
            int sizeCheck = selectedInventory.GetSize();

            if (inventorySlotCache == null || playerInventorySize != sizeCheck) 
            {
                foreach (Transform child in transform) 
                {
                    Destroy(child.gameObject);
                }

                inventorySlotCache = new Dictionary<int, InventorySlotUI>();
                playerInventorySize = sizeCheck;
                for (int i = 0; i < playerInventorySize; i++) 
                {
                    var itemUI = Instantiate(InventoryItemPrefab, transform);
                    inventorySlotCache[i] = itemUI;
                    itemUI.Setup(selectedInventory, i);
                }

                // Set content window to show top row
                transform.localPosition = new Vector3(transform.localPosition.x, 0, transform.localPosition.z);
            }

            foreach (KeyValuePair<int, InventorySlotUI> inventorySlot in inventorySlotCache) 
            {
                var itemUI = inventorySlot.Value;
                itemUI.Setup(selectedInventory, inventorySlot.Key);
            }
        }

        // ---------------------------------------------- END OF TEST --------------------------------------------------------------------------------------

But there’s a major problem. Whilst it works well for Drag and Drop, and clearly it reduces the time to drag and drop stuff around, this algorithm flat out blocks inventory slots from ever being effective for “Click-to-Equip”, after the first time we use the inventory slot. In simple terms, you click it once, it works. You click it again, it’s never going to work, regardless of what you do, and this goes for both the bank and the inventory…

What’s going on here? I’m just trying not to accidentally destroy my entire project to a point of beyond-repair

1 Like

You said you would back up the project, so it will not be beyond repair. Unfortunately, you have added so many extra things that a solution for the lag based on the standard project will require a lot of extra work. You have all of tools in front of you. Take a break, go for a walk, read a book. Do something else for an hour or two and then come back to it. Your subconscious mind works better on problems like this if you conscious mind is distracted.

I wrote that almost a day ago, Ed… Obviously I took an hour break (I just came back 30 minutes ago), and obviously I have a backup of my work :smiley: (it takes 25 minutes to make one, not 25 hours :stuck_out_tongue:)

Well that’s what living on opposite sides of the globe will do :laughing:

1 Like

wait till you see the new magic effect I discovered trying to get this algorithm to work… Inventory sizes are now getting multiplied the more I touch them

Totally nothing suspicious going on here :smiley:

kinda disagree with this one tbh… What makes a simple algorithm work, and a slightly more complex version fail? I’m 99% sure something new is responsible for this problem, but I’m struggling to wrap my head around what’s going on in that algorithm, so I don’t know how to fix it (yet)

Edit 1: OK I don’t think I see where the problem is… The half before the final ‘foreach’ loop of the new algorithm pretty much never gets called after you pickup the first item

Edit 2: Looks like it’ll only go to the first if statement when you’re picking your first item up, but most of the time it’ll just skip to the foreach loop at the bottom of the function (and the number of logs coming out of this are quite large, 100 calls, and that’s exactly 2x the size of my Player’s Inventory)… Something fishy is going on

Edit 3: Tested the system as-is outside of the engine, don’t think I need performance optimization measures tbh… I’ll just fully test the game as-is for now

Edit 4: No Shop Optimization issues either (now I remember I solved this with Brian before), just a “Shop won’t open for some reason after a bit of gameplay, unless you “Save and Quit” and return to the game” issue (haven’t solved this one with Brian yet)

I got some ally enemy logic to work. Right now, they can intelligently hunt down the enemies of the player. As simple as the algorithm was, it took me an insanely long time to get it right… Sooner or later, I’ll fix a few weird bugs and then get them to enter and leave the player’s AggroGroup through conversations

Privacy & Terms