Note : Godot 3.2.
In a previous post, I already talked about interacting with game items in 3d (mouse picking and ray casting). In this post, I'll talk a bit about various things to do with the inventory.
I created a two-part (temp) inventory gui to try and figure out the code. The inventory doesn't look pretty, but it's functional. And that's all I need for now.
The top part of the inventory is always visible. It has a menu button (top right) and 3 small buttons
to open and close the gridpanels where pickups of different types are stored (for now : books/scrolls,
magical items, and all other items).
The big circle in the middle is the (not yet functional) compass (that I think
all puzzle point&click games should have !).
The bottom part of the inventory gui can be hidden in-game. It consists of a 'detail view' area
where a larger image of the selected item is shown, together with a short description.
I haven't been able to decide yet if that's what I want, or if I prefer a fullscreen popup instead
(as in my previous game) ...
The 3 gridpanels have 10 empty slots each. At most 1 gridpanel is visible at any given time.
If the player picks up an item when the gridpanel is full, a new row of empty cells is automatically added.
If the player uses an item, these extra rows are automatically removed, until the panel has 10 cells again.
This little demo shows the interplay between pickups (PU), inventory, and interactables (IA) :
Chest (an interactable) :
The hotspot (HS) that detects the presence of the yellow key, is activated only when the player is close enough
to the chest (
'Interacting with game items in 3d'). This is indicated by a change in the mouse cursor icon ('question mark' appears).
Once the yellow key is in place, the HS is no longer active.
Key (a pickup) :
Pickups are set up just like interactables. They have only one hotspot, which lets them react to mouse clicks.
A mouse click changes the animation from 'default' to 'invisible'. An appropriate icon is shown in one of the 3
inventory panels.
Inventory :
Selecting an item in the gridpanel brings up detail information (image and description). An item in the
inventory can be dragged into the game world (notice the change in mouse cursor). 'Used' pickups disappear from
the inventory.
Assembling the GUI took a bit of experimenting, but this is the general structure I settled on.
'Items', 'books', and 'magic' are ScrollContainers.
Or rather, they are instances of a custom node ('scroll_grid') based on the built-in ScrollContainer node.
A scroll_grid has 10 instances of another custom node, called 'grid_cell'.
A grid_cell is a rather simple thing. It knows if it has its 'item' texture set
(picked something up) or if it's an empty cell.
Non-empty cells can be clicked ; this sends a signal to the inventory script.
Note that I can't send this signal straight to the inventory ; I have to make a detour via the scroll_grid.
The reason for this is that inside the inventory script I have no way of knowing the path to the grid_cell
that sent the signal. The cell could be on any of the 3 scroll_grids (items, magic, books).
This is one of the annoyances of using signals in Godot ...
And finally, let's take a look at what happens in the inventory when I click the yellow key (pickup)
in the game world, or when I drag the key over the chest lock (interactable). This code is handled in the
scroll_grid script :
And last but not least : the drag and drop functionality.
The code is actually pretty simple, but it took me some time to get it working.
Drag and drop makes use of a 'mouse_icon node' that was added to the inventory scenetree.
It is a TextureRect node that follows the mouse pointer when an inventory item is being dragged
around. It is used to show an icon of that inventory item.
All of this code goes inside the invent script ; it is called in response to the 'gui_input' signal from the detail image node.
The variables 'mi_w' and 'mi_h' in the second to last line of code refer the width and height of the mouse_icon TextureRect texture (set in _ready()).
The method check_hs_pu_match() makes use of the ray cast code I talked about in an earlier post. It checks if the interactable hotspot (HSD) has received the correct PU, and if so, it update some stuff in the inventory, and calls the do_hsd_click() method on the interactable :
The thing that tripped me up for a long time, is that (by design or not ?) mouse_over and mouse_exit events cannot be detected on world items (IA) if you started dragging from a Control element (gui). A similar issue was reported on github (#23296).
Another interesting thing that I accidentally discoverd (and the reason why I'm using a mouse_icon TextureRect node),
is that -replacing- the mouse cursor with a custom image can have unexpected results !
This is probably caused by bugs, but ... when I was testing the drag and drop code without confining
the mouse cursor to the game window (without Input.set_mouse_mode(Input.MOUSE_MODE_CONFINED)
or Input.set_mouse_mode(MOUSE_MODE_CAPTURED)), the mouse cursor would show the custom image even outside of the game window.
Testing with the game window set to half size (project setts > display window > test w/h) displayed the custom
mouse image at its orginal size instead of the expected half size.
As for the next thing to work on ... I think it's time to raise the game version from v0.1 to v0.2 and move on from code to environment (modeling). That should be somewhat of an adventure because my experience in that area is very limited. Time to watch some videos, I think.