How to Set Up Source Engine-Style Inventory Scroll?

I would like to implement an inventory system similar to the ones in Half-Life, TF2, and L4D, i.e. all of my available weapons and items (starting with one item and going up six total depending upon what items the player has picked up) are in a list on the HUD and the player scrolls to cycle through them. I found this suggestion, but the given answer has got me stuck on step three. Can anyone provide some more detail on that suggestion or perhaps an entirely new one?

Here’s what #3 of @mightyenigma 's [suggestion][1] is saying:

This will keep the value in range 0-4 as you scroll the mouse wheel up / down.

skip integers that correspond to
weapon slots that are currently empty.

Makes sense. What exactly are you slots now? How do you know that a slot is empty?

a big mess of branches depending
upon what value the integer would be
placed at upon
incrementing/decrementing and whether
or not X slot is filled and if it’s
empty increment/decrement the integer
again, but this seems like it’s not
the optimal way of going about this.

It sure would work. But we’re better than this. Let me think about it. I’ll get back to you.

I see. There are many ways of handling it. The most common one would look like this:

  • assuming here that you’re using just widgets to represent the slots
  • each slot is a separate widget that is placed (or added dynamically) to a panel
  • each slot is responsible for understanding whether it is empty or not, a slot holds onto an isEmpty boolean

When scrolling with the mouse wheel, rather than increasing the index by one, you loop against the children of the panel holding the widgets - start at the currently selected slot and check the next / previous (mouse wheel up / down) slot - does it have isEmptu {T} flag.

You keep iterating until you’ve found a non-empty one or until you’ve covered all children - you can use Get Children Count for this.

This probably sounds fairly complicated.

Thank you, that makes that a lot clearer. My next question is how best to, I guess, skip integers that correspond to weapon slots that are currently empty.

In my case, the player starts with only a sidearm that takes up slot number 2 (and I guess by extension would make it integer 1). Then as other 5 item slots are filled, more and more of the integers would become available. The only way I can think of to do this is a big mess of branches depending upon what value the integer would be placed at upon incrementing/decrementing and whether or not X slot is filled and if it’s empty increment/decrement the integer again, but this seems like it’s not the optimal way of going about this.

I have six total slots. At the start, only slot 2 is filled (and it can never be emptied). Currently I have a Boolean for whether or not each slot is filled that also sets some colors on the HUD (i.e. “No Primary Weapon”, “No Large Item”). There are also Booleans for whether or not each slot is the currently selected one to do more color changes (i.e. “Primary Weapon is Drawn”, Large Item is Drawn").

Once again, I really appreciate you taking the time to help me.

Okay, so I need to take my individual slots out of the main HUD and rebuild them as their own thing to then re-add them to the main HUD as children. Got it.

It sounds a little complicated, but maybe I can figure it out once I’ve got my HUD situated the way it needs to be.

Have a look at this, perhaps it will inspire you.

This is my slot:

Calling this event highlights the slot, making it selected. The slot has isEmpty variable so it knows whether it has something or not. And it has an exposed MyIndex variable so it knows its place in the parent panel.

The panel that holds the slots:

This panel also handles mouse wheel input:

The function fires a custom event and sends the direction of the scroll. It also informs the Player Controller that it has Handled mouse input so the controller can no longer interpret it - so you do not accidentally zoom in / out the camera when browsing items, for example.

And here’s the iteration loop, only one direction because I run out time:

Image from Gyazo


It needs more work, the mouse scrolls in the wrong direction for example. It’s a matter of ticking some bools in the mouse input I think. And you’d need to add the index finding in the opposing direction.

I think it could be a half-decent start. I’ve made carousel menus before but never one that has to skip elements. That’s how I’d approach it and it seems to be working. Promising.

Do tell if any of the elements are odd-looking.

Thank you for going into so much detail, but I’m still super confused. (Sorry!)

So I change my slots to get their color in the event graph as per your example. That seems to work fine. My problems begin when I try to do the next step.

I think one of my problems might be that you seem to be using the same widget as the basis for every slot in your inventory but each of my slots are their own blueprints because they have entirely different setups. When I added my slots to the viewport, I think I got six extra copies of each slot (in addition to the original copies even though I never specifically added them to the viewport in the first place so I don’t know what’s going on). My best attempt at coming up with my own solution gave me this, which I would think would add everything to my array just fine but apparently it’s not.

(Edit: Just noticed that the second “Primary” node should be a “Melee” Node, but I high doubt it’s making any sort of difference.)

For some reason, I can’t for the life of me get my array to allow me to reference the “Is Selected” function from my Sidearm (the slot I’m testing ATM). When I try to pull off the Selected Slot node it tells me that “User Widget object reference is not compatible with Sidearm object reference”. And this is of course extending down to later on, like right after the For Each Loop With Break where you get the selected slot’s “Is Empty” Boolean and plug it into a Branch. I can’t make the connection.

I’m really sorry if this is a major noob problem (which it probably is).

I think one of my problems might be
that you seem to be using the same
widget as the basis for every slot in
your inventory but each of my slots
are their own blueprints because they
have entirely different setups.

Pretty much, yes. One slot for everything because slots are identical, the items the slots hold are not, though.

Can you roughly clarify what is different about each slot? Perhaps I’m misunderstanding something and your vision is more complex than I think.


Generally speaking, you’d want to make slots the same class - the same widget blueprint and duplicate it (either manually like you do, or automatically like I did).

That is because all slots should share certain common functionality like highlights, sounds, animations, ability to display tooltips about the held item, having the empty / full state and so on; and you definitely do not want to manually repeat the script that does all that for each slot.

A slot can have a variable (enumerators work well here) that dictates what item it can hold, and reference an Item the player carries.

This allows you to tell this generic slot to DropItem, ShowHighlight or play ‘Beep’ sound when the mouse enters, no matter what item it holds. It’s a layer of abstraction.


The same goes for the actual items. They should share a base Item class because player can Use, Drop and Place them in the inventory and Equip them. Primary and Sidearm inherit from Item class and create Firearms subclass (that can fire bullets), Grenades inherit from Items but are so different from Firearm subclass that they deserve their own subclass, and so on.

This is what I’m trying to get. (I’m working on a L4D fangame, so that’s why I’m trying to replicate this type of HUD.)

277378-inventory-2.png

I can definitely see where the bottom three could be easily based off of the same slot widget but I foresee the top three being a little bit more work. I’m guessing I should just build the base widget with all of the setups needed for every single slot and then have a check for which slot is actually used and disable everything that doesn’t pertain to that slot?

I’m guessing I should just build the
base widget with all of the setups
needed for every single slot and then
have a check for which slot is
actually used and disable everything
that doesn’t pertain to that slot?

This is would work and allows you to treat all of them in a similar fashion.


Another method is via an Interface - it allows for communication between classes that are not related.

You can have 6 completely different widgets implementing the same interface - you ask a widget to do a thing - and each will have a unique version of the action required. But it still requires you to duplicate / write code 6 times.

On the other hand, 6 times is not the worst thing in the world. If you later on decide that you need to refactor something, you’ll need to deal with the consequence of doing multiple times, though.

Okay, so I was getting quite frustrated and decided to put this particular problem aside for a couple of days to let it marinate in the back of my mind.

After lots of thinking, experimenting, and looking at multiple inventory system tutorials, I think I have my inventory working visually. All of my individual slots are the same widget, with my specific setups for each panel category being enabled or disabled according to the slot’s index value and a category Enum. I have copied your provided setup except for two things. Going off of [this][1] tutorial, I manually placed my slots into my Inventory widget where I can manually assign them the desired index values. This bit seems to work perfectly fine so far.

(I just realized this looks exactly the same as my last attempt, but my widget references are referring to my manually-placed (and named) slots that are all derived from the same widget this time as opposed to all being different widgets like last time.)

The only other thing I’ve changed is that my “Slot Is Selected” event toggles an “Is Selected” Boolean which is one of the variables that set my slots’ colors as needed. Your mouse wheel function is copied over as-is, and I’ve also managed to solve my array-setting-and-calling problems so that I was able to copy your iteration setup as well.

It’s still not working, though. I’ve tried attaching a print string to my “Is Selected” even toggle to see if something happens, but I’m not getting anything. I’ve been trying different things to try and fix it myself, but I think one of the things confusing me is that I’m not entirely sure what the purpose of your Int Map array is, and I suppose I’m also not sure if I’ve even set it up the right way (I just created a new array of integers).

the purpose of your Int Map

It’s just an empty array of ints that tells us the order of the slots to step through, looking at you Make Array node:

  • if you start at slot 3 and want to check all slots (to see whether they’re empty) in the down direction, you need to check slots 4,5,0,1,2.
  • if you start at slot 4 and want to search up, you need to check slots 3,2,1,0,5.

As you see, the value has to be wrapped - that’s what modulo does. It creates an ordered list for the loop that follows to step through, so it does not miss an element. It would not be necessary if you just wanted to scroll up / down without wrapping from the beginning / end.

We have 6 slots and each slot knows where in the array it sits (myIndex) - I did this automatically by spawning slots and assigning this variable

For this to work the MyIndex variable has to be Set for each slot. 0 for slot 0 and 5 for slot 5 - I do it automatically via an exposed variable in the 2nd pic; you can do it manually since it’s only 6 slots. Or you can run ForEach loop off the AllSlots array.

If in doubt, post a screenshot, perhaps I can poke holes in it.

This is getting so unbelievably frustrating…

This is the only connected thing in the event graph of my slot widget. My images and text are set to derive their color from a binding that checks, among other things, whether or not this “Is Selected” Boolean is true or false.

278170-inventory-4.png

This is the first thing in the event graph of my Player Inventory widget. I removed my previous setup and redid it to match yours aside from the testing-related stuff.

Everything seems to work so far. This is my OnMouseWheel override function. Literally copied one-to-one from your example.

And last but not least, here is my iteration stuff which I’m pretty sure is the exact same as yours.

I scroll my mouse both ways, and literally nothing seems to change. I can’t even get a print string to spit out a line to confirm that something is happening. This project is making me want to cry so bad right now. :')

This project is making me want to cry
so bad right now. :')

Ah, welcome to blueprint scripting. It all looks good at a glance.

I can’t even get a print string to
spit out a line to confirm that
something is happening.

Let’s focus on on printing the string first. I’ll assume that you create and add the widget to the viewport. and the Input Mode is not set to Game Only.

It should start working as soon as you navigate to it and click inside it - this puts the widget in focus so it can start intercepting input before the Player Controller get its chance.

Let me know if this works and mouse scroll prints.

For adding the HUD to the viewport, my “On Begin Play” in my character’s BP is set to create the HUD widget as the very first thing and add it to the viewport. I don’t have any sort of input mode setting.

This might be one of my problems with following your example: my HUD is not meant to be directly interacted with by the player aside from scrolling through inventory slots (which automatically draws the item in the selected slot without requiring any clicking). The scroll wheel doesn’t control anything else in the game unless the player is in a menu screen.

I don’t have any sort of input mode
setting.

That’e fine.

This might be one of my problems with
following your example: my HUD is not
meant to be directly interacted with
by the player aside from scrolling
through inventory slots (which
automatically draws the item in the
selected slot without requiring any
clicking).

I see, we can work around this easily, the input is currently implemented in the widget itself; when you interact with the game world, the widget will not have focus and not receive the scroll. Thanks for clarifying this.

For now (to test), can you just ensure that the root of the widget is Visible, click anywhere on the widget and use the scroll wheel. That should work. See if the scroll events print.

Regarding the lack of interactivity with this menu, rather than overriding onMouseWheel in the widget, move this functionality to the player controller and have it call the widget’s custom event.

Okay, so some good things happened and some bad things happened.

As you suggested, I moved the Mouse Wheel override over to the event graph of my Player Controller and connected it up to my Inventory Scroll axis mapping event (and deleted it from my Inventory widget).

I successfully got strings to print from this. However, I also got errors.

My Mouse Scrolled event is still in my Inventory widget’s event graph, exactly where it was before and still an exact copy of yours.

You never set a reference to the SurvivorHUD, it seems. Creating a variable is just that, it holds no value. Imagine an int variable but it has no value (not even 0!).

You can either create the widget in the controller and Set SurvivorHUD, or have the blueprint that creates the widget cast to the Player Controller set the SurvivorHUD - this variable has to point at a valid object.