Blocking Input Propagation in Unity
In the gaming world, making sure players smoothly interact with both the interface (UI) and the game itself is a big deal. On that note, at the time of writing this post - it can be a little tricky to manage input blocking and handling input propagation in the Unity Gameface/Prysm plugin. So, let’s dive into a practical solution that lets developers stop user input from accidentally messing with the game.
The suggested solution below covers the general steps to block all game input and let the UI handle it. In the end, you’ll also find a way to still handle the input in the game, in case the UI doesn’t need to process anything.
To start with some general information about the current input handling we do – our SDK is hooked onto the Unity input, and when the engine receives an input event - we check if there are any active Views, and send over the event data to them. After that, Unity proceeds to handle the input events.
On the other hand - with the changes below, the plugin receives the necessary event data, then clears the input data in Unity by calling the ResetInputAxes function(which will reset all input for the frame so nothing is propagated), and finally proceeds to handle the already gathered events for the UI.
The specific code changes to apply such an option are as follows:
1. Expose the option for ease of use in the CohtmlView component under a Block Input Propagation check box in order to determine whether that View will block further input propagation.
The code changes should be added to the CohtmlView.cs file (e.g. somewhere after the public bool RaycastTarget):
[HideInInspector]
[SerializeField]
private bool shouldBlockInputPropagation = false;
/// <summary>
/// Determines whether this view will block input propagation to the game.
/// </summary>
/// <value>
/// <c>true</c> if this view blocks input propagation; otherwise, <c>false</c>.
/// </value>
[ExposeProperty(Category = ExposePropertyInfo.FoldoutType.Input,
PrettyName = "Block Input Propagation",
Tooltip = "Determines whether this view will block input propagation to the game.",
IsStatic = false)]
public bool ShouldBlockInputPropagation
{
get => isActiveAndEnabled && shouldBlockInputPropagation;
set => shouldBlockInputPropagation = value;
}
2. Add the functionality to actually block input propagation.
The logic changes here will be in the CohtmlInputHandler.cs file inside the SendEventToViews method overloads. More specifically -- we need to check whether the gathered Views have ShouldBlockInputPropagation enabled, and call ResetInputAxes if that's the case. This can be added both for Mouse input(the PointerEvent overload below) and for Touch input (the SendEventToViews(TouchEventCollection touches) overload)
Mouse:
public void SendEventToViews(PointerEvent eventData, InputEventWrapper unityEvent)
{
...
m_GatheredEventViews.Clear();
GatherOnScreenViews();
for (int i = 0; i < m_GatheredEventViews.Count; i++)
{
eventData.AddView(m_GatheredEventViews[i], eventData.InvertY);
// Input propagation change begin
if(m_GatheredEventViews[i].ShouldBlockInputPropagation)
{
UnityEngine.Input.ResetInputAxes();
}
// Input propagation change end
}
...
For Touch input:
public void SendEventToViews(TouchEventCollection touches)
{
m_GatheredEventViews.Clear();
GatherOnScreenViews();
for (int i = 0; i < m_GatheredEventViews.Count; i++)
{
for (uint j = 0; j < touches.Capacity; j++)
{
if (touches[j].IsActive)
{
touches[j].AddView(m_GatheredEventViews[i], touches[j].InvertY);
}
}
// Input propagation change begin
if (m_GatheredEventViews[i].ShouldBlockInputPropagation)
{
UnityEngine.Input.ResetInputAxes();
}
// Input propagation change end
}
...
And for Keyboard:
public void SendEventToFocusedView(KeyEvent eventData)
{
if (FocusedView == null)
{
return;
}
m_GatheredEventViews.Clear();
m_GatheredEventViews.Add(FocusedView);
// Input propagation change begin
if (m_GatheredEventViews[0].ShouldBlockInputPropagation)
{
UnityEngine.Input.ResetInputAxes();
}
// Input propagation change end
PropagateEventToGatheredViews(eventData);
}
After those changes, the end result should be as in the gif below(depending on the Block Input Propagation value on the current View):

The solution above is to basically send the input to Gameface and then clear the received input entirely for that frame. This means that the game scene in the back(e.g. the rotated cube in the above gif) and the Unity UI itself will not receive any input events in this case(ShouldBlockInputPropagation = true). In case you are using EventSystem callbacks – you could also disable the Event System temporarily if necessary(e.g. obtain it and do eventSystem.SetActive(false);) since the ResetInputAxes call won’t disable those.
In case we want to take this one step further – we can still add a way to handle any input events that were not handled by the Gameface View and execute any desired game logic for the unhandled input event. The implementation for the handling of those events is entirely up to the user and is described in detail in this section of the documentation page and more specifically - the Unhandled Events part.
Either with Screen UI(HUD-like, CohtmlView on the Main Camera) or the Unity UI + CohtmlView(click) -- as established, enabling the ShouldBlockInputPropagation option we implemented earlier will block all input apart from the one handled by Gameface. However - in case the input is not handled by Gameface and if the event is handled by an HTML element with a Ignored CSS class (see documentation here and the screenshot below) - the On<>TargetNotFound callbacks will be invoked, depending on the received input type.
We can subscribe to those callbacks by including the cohtml input handler in the C# script and creating a handler for the input type, e.g. for the OnMouseEventTargetNotFound :
using cohtml.InputSystem;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
#endif
...
private void Start()
{
CohtmlInputHandler.OnMouseEventTargetNotFound = OnMouseEventTargetNotFound;
{
private void OnMouseEventTargetNotFound(InputEventWrapper inputEventWrapper)
{
// Handle event
...
}
Essentially, this way you can implement handling input in the game logic(with ShouldBlockInputPropagation set to on), in the case when the input was not actually handled by the Gameface View. (e.g. the user clicked on some empty space without UI elements)
Below is some very minimal logic showcasing an example of handling the input both in the UnityUI and/or in the game scene, and generally, the input can be processed as desired to fit the use case.
Unity UI example:
private void OnMouseEventTargetNotFound(InputEventWrapper inputEventWrapper)
{
/// input manager case
Ray ray = mainCamera.ScreenPointToRay(inputEventWrapper.Event.mousePosition);
PointerEventData eventData = new PointerEventData(eventSystem);
eventData.position = Input.mousePosition;
// Perform the raycast
List<RaycastResult> results = new List<RaycastResult>();
raycaster.Raycast(eventData, results);
foreach (var result in results)
{
// Handle the UI element that was hit
// Should check for type, etc., ensure not to handle unnecessary events or handle input multiple times
Debug.Log("Raycast hit: " + result.gameObject.name);
// logic
}
Game scene example(both Input Manager and Input System cases for reference)
private void OnMouseEventTargetNotFound(InputEventWrapper inputEventWrapper)
{
Ray rayPoint = new Ray();
#if ENABLE_INPUT_SYSTEM
if (inputEventWrapper.Event.control is ButtonControl)
{
Vector2 position = ((Pointer)inputEventWrapper.Event.control.device).position.ReadValue();
rayPoint = Camera.main.ScreenPointToRay(position);
}
#else
if (inputEventWrapper.Event.clickCount == 1)
{
Vector2 mousePosition = inputEventWrapper.Event.mousePosition;
rayPoint = Camera.main.ScreenPointToRay(new Vector2(mousePosition.x, Screen.height - mousePosition.y));
}
#endif
RaycastHit hit;
if (Physics.Raycast(rayPoint, out hit, 1000))
{
Debug.Log(hit.point);
// logic
}
}
Finally, you can also check out the InputPropagation sample that is distributed with the Gameface package for more examples and different usages of this type of input handling both in the front end and engine side.
Please sign in to leave a comment.
Comments
0 comments