Inventory Context Menu Support
Right click --> Unequip, right click --> Drop... Wait, I wanted to consume you!
In this tutorial, I want to show you how to modify the Game Creator inventory to support a context menu when clicking the right mouse button and do the most common things ((Un-)Equip, Consume, Drop).

Prerequisites

  • Install Unity
  • Install IDE (I use Visual Studio 2019)
  • Import Game Creator
  • Import and enable the Inventory module

General

Many games (like Zelda: Breath of the Wilds) use a context menu with buttons to do stuff, like equipping weapons, dropping items etc. The Game Creator Inventory doesn't support this out of the box. There are several changes that need to be done in order to support a context menu:
  • Modify the GC UI Button script to differentiate between right and left mouse clicks
  • Replace the Button component in the item prefab with the GC UI Button
  • Add a prefab for the context menu with buttons
  • Add a script to the prefab which stores references to the buttons (prefab and script attached in zip)
  • Modify the GC ItemUI script to add the necessary functionality for a context menu
  • Assign the variables in the modified ItemUI script
Make sure to have a backup of your project before doing anything of this, so you can revert changes made to the scripts!

Adding Mouse Right Click Support

There is currently no differentation between a left and a right mouse click on a button in Unity. Game Creator has its own UI button with additional functionality to the basic Unity UI Button. Open the ButtonActions script, you can find it under Assets --> Plugins --> GameCreator --> Core --> Mono --> UI --> ButtonActions.cs.

Using statements

Add the following two using statements (above the Unity Editor directive):
1
using UnityEngine.EventSystems; // Already exists, only for clarity where to put it
2
using UnityEngine.Serialization;
3
using static UnityEngine.UI.Button;
Copied!

Add right click button event

Add the following content after public Actions actions = null; and before public ButtonActionsEvent onClick...in the PROPERTIES section:
1
public Actions actions = null; // Already exists
2
3
[FormerlySerializedAs("onClickRight")]
4
[SerializeField]
5
public ButtonClickedEvent onClickRight = new ButtonClickedEvent();
6
7
public ButtonActionsEvent onClick = new ButtonActionsEvent(); // Already exists
Copied!

Modify the OnPointerClick() method

Modify the method OnPointerClick(PointerEventData eventData) in the INTERFACES section:
1
public virtual void OnPointerClick(PointerEventData eventData)
2
{
3
if (eventData.button == PointerEventData.InputButton.Left)
4
{
5
this.Press();
6
}
7
else if (eventData.button == PointerEventData.InputButton.Right)
8
{
9
this.PressRight();
10
}
11
}
Copied!

Add the PressRight() method

Add the method PressRight() in the PRIVATE METHODS section:
1
private void PressRight()
2
{
3
if (!IsActive() || !IsInteractable()) return;
4
if (this.onClickRight != null) this.onClickRight.Invoke();
5
}
Copied!
The || you see in the first if statement is a so called conditional logical OR operator: Reference

Change the look of the Button Inspector

Add a serialized property

The GC UI Button layout is being replaced by a custom one. I have a tutorial on this too (Custom Editor Windows - Part 2). Open Assets --> Plugins --> GameCreator --> Core --> Editor --> UI --> ButtonActionsEditor.cs.
Add a Serialized Property for the OnClickRight event:
1
private ActionsEditor editorActions; // Already exists
2
SerializedProperty spOnClickRight;
Copied!

Assign the added serialized property

Add this line of code to the OnEnable() method after base.OnEnable() and before SerializedProperty spActions... :
1
spOnClickRight = serializedObject.FindProperty("onClickRight");
Copied!

Modify OnInspectorGUI()

Modify the OnInspectorGUI() method to look like this:
1
public override void OnInspectorGUI()
2
{
3
base.OnInspectorGUI();
4
EditorGUILayout.Space();
5
6
serializedObject.Update();
7
8
EditorGUILayout.LabelField("Left click");
9
if (this.editorActions != null)
10
{
11
this.editorActions.OnInspectorGUI();
12
}
13
14
EditorGUILayout.Space();
15
16
EditorGUILayout.LabelField("Right click");
17
EditorGUILayout.PropertyField(spOnClickRight);
18
serializedObject.ApplyModifiedProperties();
19
}
Copied!

Replace the button component

Now open an item prefab like the ItemRPG or ItemAdventure (depending on what skin you want to use), delete the button component on the root object and add the GC UI Button that we just modified. Add the action Call Method to the left click and add a new entry to right click. It should then look like this:
You don't have ItemUI.OnClickRight() as of now, that changes in one of the next sections. Don't forget to set this when we add it.

Import ContextMenu Prefab And Script

I have prepared a script and a prefab for this. Download this zip file, extract it, import first the script then the prefab package into your project and you should be good:
ItemContextMenuUI.zip
4KB
Binary
The prefab is fully customizable, the script is necessary to hold a reference to the buttons. Make sure that the buttons are assigned to the variables:
In order to make sure that the references are correct and not somehow corrupted, clear them all (set to none) and readd them.

Add Context Menu Functionality

Add a property to the InventoryManager

In order for the context menu to work correctly, we need to add a variable to hold a reference of it to the InventoryManager script. Open Assets --> Plugins --> GameCreator --> Inventory --> Mono --> Utilities --> InventoryManager.cs and add the following line to the PROPERTIES section:
1
[HideInInspector] public GameObject itemContextMenu;
Copied!

Add Properties to the ItemUI script

Now let's add the functionality for the context menu. Open the item prefab you chose above and edit the ItemUI script. Add the following code below public GameObject equipped; in the PROPERTIES section:
1
[Space]
2
public RectTransform itemTransform;
3
public GameObject prefabContextMenu;
4
5
[HideInInspector]
6
public GameObject itemContextMenu;
Copied!

Add Methods to the ItemUI script

Add the System namespace as a using statement: using System; Add the following line below InventoryManager.Instance.ConsumeItem(this.item.uuid); in public virtual void OnClick() to disable the context menu when you click on an item:
1
if (InventoryManager.Instance.itemContextMenu != null) HideItemContextMenu();
Copied!
Then add all of the following code after public virtual void OnClick() and before public virtual void OnDragBegin() . The explanation to every method and its functionality is below the code block.
1
public virtual void OnClickRight()
2
{
3
Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
4
ShowItemContextMenu(this.item.uuid, this.item.itemTypes, prefabContextMenu, itemTransform);
5
}
6
7
public virtual void ShowItemContextMenu(int uuid, int itemTypes, GameObject prefabContextMenu, RectTransform itemTransform)
8
{
9
if (this.itemContextMenu == null && InventoryManager.Instance.itemContextMenu == null)
10
{
11
this.itemContextMenu = Instantiate(prefabContextMenu, itemTransform);
12
this.itemContextMenu.transform.localScale = Vector3.one;
13
14
ItemContextMenuUI itemContextMenuUI = this.itemContextMenu.GetComponent<ItemContextMenuUI>();
15
16
AddButtonListeners(itemContextMenuUI, uuid, itemTypes);
17
18
SetButtonVisibility(uuid, itemContextMenuUI);
19
20
InventoryManager.Instance.itemContextMenu = this.itemContextMenu;
21
}
22
else if (this.itemContextMenu == null && InventoryManager.Instance.itemContextMenu != null)
23
{
24
HideItemContextMenu();
25
this.itemContextMenu = Instantiate(prefabContextMenu, itemTransform);
26
this.itemContextMenu.transform.localScale = Vector3.one;
27
28
ItemContextMenuUI itemContextMenuUI = this.itemContextMenu.GetComponent<ItemContextMenuUI>();
29
30
AddButtonListeners(itemContextMenuUI, uuid, itemTypes);
31
32
SetButtonVisibility(uuid, itemContextMenuUI);
33
34
InventoryManager.Instance.itemContextMenu = this.itemContextMenu;
35
}
36
else
37
{
38
HideItemContextMenu();
39
this.itemContextMenu.SetActive(true);
40
41
ItemContextMenuUI itemContextMenuUI = this.itemContextMenu.GetComponent<ItemContextMenuUI>();
42
43
SetButtonVisibility(uuid, itemContextMenuUI);
44
45
InventoryManager.Instance.itemContextMenu = this.itemContextMenu;
46
}
47
}
48
49
public virtual void HideItemContextMenu()
50
{
51
try
52
{
53
InventoryManager.Instance.itemContextMenu.SetActive(false);
54
}
55
catch
56
{
57
InventoryManager.Instance.itemContextMenu = null;
58
}
59
}
60
61
public virtual void AddButtonListeners (ItemContextMenuUI itemContextMenuUI, int uuid, int itemTypes)
62
{
63
Button btnEquip = itemContextMenuUI.buttonEquip.GetComponent<Button>();
64
Button btnUnequip = itemContextMenuUI.buttonUnequip.GetComponent<Button>();
65
Button btnConsume = itemContextMenuUI.buttonConsume.GetComponent<Button>();
66
Button btnDrop = itemContextMenuUI.buttonDrop.GetComponent<Button>();
67
68
btnEquip.onClick.AddListener(() => itemEquip(uuid, itemTypes));
69
btnUnequip.onClick.AddListener(() => itemUnequip(uuid));
70
btnConsume.onClick.AddListener(() => itemConsume(uuid));
71
btnDrop.onClick.AddListener(() => itemDrop(uuid, itemTypes));
72
}
73
74
public virtual void SetButtonVisibility(int uuid, ItemContextMenuUI itemContextMenuUI)
75
{
76
Item item = InventoryManager.Instance.itemsCatalogue[uuid];
77
Item itemEquipped = null;
78
79
if (item.itemTypes >= 0)
80
{
81
itemEquipped = InventoryManager.Instance.GetEquip(HookPlayer.Instance.gameObject, Convert.ToInt32(Math.Log(item.itemTypes, 2)));
82
}
83
84
GameObject buttonEquip = itemContextMenuUI.buttonEquip;
85
GameObject buttonUnequip = itemContextMenuUI.buttonUnequip;
86
GameObject buttonConsume = itemContextMenuUI.buttonConsume;
87
GameObject buttonDrop = itemContextMenuUI.buttonDrop;
88
89
if (item.equipable && itemEquipped == null)
90
{
91
buttonEquip.SetActive(true);
92
buttonUnequip.SetActive(false);
93
buttonDrop.SetActive(true);
94
}
95
else if (item.equipable && itemEquipped.uuid != item.uuid)
96
{
97
buttonEquip.SetActive(true);
98
buttonUnequip.SetActive(false);
99
buttonDrop.SetActive(true);
100
}
101
else if (item.equipable && itemEquipped.uuid == item.uuid)
102
{
103
buttonEquip.SetActive(false);
104
buttonUnequip.SetActive(true);
105
buttonDrop.SetActive(false);
106
}
107
else
108
{
109
buttonEquip.SetActive(false);
110
buttonUnequip.SetActive(false);
111
}
112
113
if (item.consumeItem)
114
{
115
buttonConsume.SetActive(true);
116
}
117
else
118
{
119
buttonConsume.SetActive(false);
120
}
121
}
122
123
public virtual void itemEquip(int uuid, int itemTypes)
124
{
125
InventoryManager.Instance.Equip(HookPlayer.Instance.gameObject, uuid, Convert.ToInt32(Math.Log(itemTypes, 2)));
126
127
HideItemContextMenu();
128
}
129
130
public virtual void itemUnequip(int uuid)
131
{
132
InventoryManager.Instance.Unequip(HookPlayer.Instance.gameObject, uuid);
133
134
HideItemContextMenu();
135
}
136
137
public virtual void itemConsume(int uuid)
138
{
139
InventoryManager.Instance.ConsumeItem(uuid, HookPlayer.Instance.gameObject);
140
141
HideItemContextMenu();
142
}
143
144
public virtual void itemDrop(int uuid, int itemTypes)
145
{
146
Vector3 position = HookPlayer.Instance.transform.TransformPoint(Vector3.forward * 1f);
147
148
Instantiate(this.item.prefab, position, Quaternion.identity);
149
InventoryManager.Instance.SubstractItemFromInventory(this.item.uuid, 1);
150
151
HideItemContextMenu();
152
}
Copied!
Method
Functionality
OnClickRight()
Gets executed when a right click on an item is registered.
ShowItemContextMenu()
Checks if a context menu is already added to the item and if a context menu was already opened, if not, then it instantiates one and adds it as a reference to the InventoryManager. This is necessary to close the context menus when you click on any item or open a context menu on another item.
HideItemContextMenu()
Tries to set the current context menu to inactive. This results in an error when you remove the last item of an item from the inventory. If this happens, it then sets the variable to null.
AddButtonListeners()
This adds a non-persistent listener for each button in the context menu of an item. The strange way of writing a listener <variableName>.AddListener(() => Method(argument1, argument2)) allows you to add arguments to a method that gets executed when the event is called. Otherwise you just write <variableName>.AddListener(MethodName). Note that you don't use the () brackets after the method name in this way.
SetButtonVisibility()
Gets the current selected item and compares different properties of it to display the suitable buttons. You don't want the button Equip when you already have equipped this item.
itemEquip()
Gets called when the button Equip is pressed. Equips the item.
itemUnequip()
Gets called when the button Unequip is pressed. Unequips the item.
itemConsume()
Gets called when the button Consume is pressed. Consumes the item.
itemDrop()
Gets called when the button Drop is pressed. Instantiates the item in front of the player with a distance of 1 and subtracts one. The distance value is hardcoded, but you can change it to any value you want.
Don't forget to assign the OnClickRight() method to the right click event field of the GC UI Button in the item prefab!

Assign Variables In The Item Prefab

The only thing left to do is add a value for the variables itemPrefab and prefabContextMenu in your chosen item prefab:

Test the functionality

Test if everything is working as intended:
  • Context Menu appears when clicking the right mouse button on an item
  • Context Menu disappears when clicking the right mouse button on another item and a new context menu appears on the other item
  • Context Menu disappears when clicking the left mouse button on any item
  • (Un-)Equipping, consuming and dropping is working
It is not perfect, i.e. the context menu remains active when you close and reopen the inventory window. In order to iron out this behavior, further customization is needed, which I won't cover in this tutorial. It is possible that there will be a tutorial on how to do this in the future, but not now.
Last modified 8mo ago