As part of the process of getting the project ready for the xBox certification tests, the most common issue that keeps on popping up in the game is misleading GUI and/or unresponsive buttons. What this means is that if the game contains any GUI that tells the player that he is able to perform a certain action with a specific button on the controller, but pressing the button does nothing, the game fails to pass the test. Unfortunately, since the game was designed for PC and did not have controller support before Codeglue has taken the project, unresponsive GUI is still common. That is mostly due to the project being so large, which makes it hard to test all aspects of the game and account for every possible case.

One such GUI issue was the player’s inventory menu. In the inventory the player has the option to select an item and press Y on the xBox controller to view some information about the item. If the player launches the main menu which contains the inventory and ends up in the inventory popup directly after launching the menu, this button works perfectly fine. However, if the player then decides to change to another tab in the main menu, such as the quests tab, and then return back to the inventory, the button mysteriously stops working. If the player then closes the inventory and reenters the inventory, it magically returns to work!

I was not the one to discover this issue, instead it was another programmer on my team, and when he reported the issue on Mantis, he had done some investigation, and discovered the following:

“The function SendMessage in MessageSystem.cs normally sends a callback to the InventoryTab function “OnInteractClicked” when entering the inventory tab
When coming from the quest tab, the callback doesn’t happen.
This results in the “firstPress” bool not being updated correctly.
The “firstPress” bool can make the function “OnViewInfoClicked” (which is used to view an item) return instantly.”

Upon further inspection, I have discovered that the callback does not happen when entering the inventory from any other tab in the main menu. Based on the information the other programmer provided, I thought that the best fix to the issue would be to properly update the firstPress variable so that the OnViewInfoClicked method no longer immediately returns due to the variable having the wrong value. I noticed that even though the OnInteractionClicked method would not always be called, the Enable method of the InventoryTab is called every time the tab is opened, so I decided to reset the firstPress variable during the call to the Enable method. This did fix the viewing info issue, however, it did result in another issue which the firstPress variable was attempting to fix – if the player wants to open the inventory tab, he has to press the A button on the controller (the interact button) in order to focus on the inventory. Unfortunately, this also results in the game executing code for purchasing the currently highlighted item in the player’s inventory.

I then remembered the lesson I learned from fixing the Large Tile issue, which says that I should always look for the simple more abstract solution that is not directly focused on the issue at hand. After some more thought, I started wondering how is it that the callback sometimes happens and sometimes doesn’t. I took a look into the MessageSystem class and there I looked into how the callbacks are handled.

In order for a class to receive callbacks, it must register a method of choice to the MessageSystem class using an ID to identify a handler, associated with a button press on the controller. In other words, if the InventoryTab wanted to receive a callback when the interaction button is pressed on the controller, it needs to call the register method in the MessageSystem with the InteractionClick ID and the method that it wants called.

static public void RegisterListener(string messageName, GameObject gameObject, Action callback, int order = 0) // Higher order gets called first
{
	if (!handlers.ContainsKey(messageName))
		handlers.Add(messageName, new List());

	List messageHandlers = handlers[messageName];
	messageHandlers.Add(new Handler() { gameObject = gameObject, callback = callback, order = order });
	messageHandlers.Sort((a, b) => b.order.CompareTo(a.order));
}

 

The MessageSystem then goes through all the handlers, and for each handler list, it retrieves the gameobject that is registered with that specific handler, checks whether the gameobject is active in the hierarchy, and then calls the provided method.

static public void SendMessage(string messageName)
{
	List handlerList = null;
	if (!handlers.TryGetValue(messageName, out handlerList))
		return;

	for (int i = 0; i < handlerList.Count; ++i)
	{
		Handler handler = handlerList[i];
		if (handler.gameObject.activeInHierarchy)
		{
			handler.callback();
		}
	}
}

 

The key to the issue is the check whether the gameobject is active in the hierarchy. After seeing this piece of code, I have inspected the scene hierarchy, and when the player launched the main menu and ends up immediately in the inventory, the inventorytab gameobject is active in the hierarchy, and the view info button works perfectly fine. However, as the player changes to another tab in the main menu, the inventory tab game object is disabled, and thus the tab does not receive any callbacks. When the player returns to the inventory tab once again, the game object gets reenabled once again in the hierarchy, but the issue is that the callback still doesn’t happen. Here I thought to take a look at the class that owns the InventoryTab and that enables and disables it. This was the InventoryPopup class. That class was also registered to receive OnInteractionClicked callbacks, and in that callback it would enable the selected tab. So, the normal process would look as follows:

The player opens up the main menu (InventoryPopup). The player would highlight the tab he would like to view (InventoryTab), and he would then press A on the controller (Interaction callback). The callback would tell the inventory popup to enable the highlighted tab, and then the callback would tell the tab to do whatever it needed to do, like setting the firstPressed value. Since this process did not happen when going to the inventorytab from any other tab, my suspicion was that the MessageSystem first sent a callback to the inventory tab, which at the time of sending the callback, its gameobject was inactive, so the callback was never sent, and only then it sent a callback to the inventory popup which enabled the inventory tab gameobject.

If that was the case, the solution to the problem is very simple – I needed to make it so that the inventorypopup callback is always handled first, and only then are the tabs handled. Fortunately the message system already had the API for such a case – when registering a new listener, there is an optional variable called order. A higher order value means that the callback is handled first. The default value for the order variable is 0, therefore, I have simply made it so that when the InventoryPopup registers for the callback, it has an order of value 1, and thusly, the InventoryPopup handles the interaction first, enables the highlighted tab, and then the tab receives the callback, and handles it’s own code. And thus, by adding 2 extra characters to the code, a very complex and certification failing issue was fixed.