Skip to main content
New Participant
November 10, 2022

Pointable Canvas menu in Unity stops working when user uses both hands or controllers

In the unity oculus interaction SDK there is a bug in the PointableCanvasModule

 

This is the ADB log

 

15:37:50.067 Unity InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.Collections.Generic.Dictionary`2+ValueCollection+Enumerator[TKey,TValue].MoveNext ()
at Oculus.Interaction.PointableCanvasModule.Process () [0x00000]
at UnityEngine.EventSystems.EventSystem.Update () [0x00000]

 

The result is the menu items are no longer selectable and the user has a stuck menu

 

Offending code:

 

public class PointableCanvasModule : PointerInputModule 
////...
public override void Process()
        {
            foreach (Pointer pointer in _pointersForDeletion)
            {
                ProcessPointer(pointer, true);
            }
            _pointersForDeletion.Clear();
 
            // when pointers/hand rays are added: InvalidOperationException: Collection was modified
            foreach (Pointer pointer in _pointerMap.Values) 
            {
                ProcessPointer(pointer);
            }
        }

 Proposed fix

var pointers = new List<Pointer>();
pointers.AddRange(_pointerMap.Values);
foreach (Pointer pointer in pointers)
  ProcessPointer(pointer);

 

 

    This topic has been closed for replies.

    1 reply

    New Participant
    November 10, 2022

    Created workaround by adding the below class and using that instead of the standard PointableCanvasModule

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Oculus.Interaction
    {
        public class PointableCanvasModulePatched : PointableCanvasModule
        {
            /// <summary>
            /// Patch for bug https://forums.oculusvr.com/t5/Unity-VR-Development/Pointable-Canvas-menu-in-Unity-stops-working-when-user-uses-both/td-p/997121
            /// Requires members accessed in this method to manually be made non-private
            /// </summary>
            public override void Process()
            {
                // Clone deleted pointers into in a local array to prevent
                // InvalidOperationException: Collection was modified; enumeration operation may not execute.
                var pointersToLoopOver = _pointersForDeletion.ToArray();
                // Clear the deleted pointers list so it can start collecting newly delete pointers if those occur as a side effect to this method
                _pointersForDeletion.Clear();
                foreach (Pointer pointer in pointersToLoopOver)
                    ProcessPointer(pointer, forceRelease: true);
    
                // Clone current pointers into a local array to prevent
                // InvalidOperationException: Collection was modified; enumeration operation may not execute.
                pointersToLoopOver = _pointerMap.Values.ToArray();
                foreach (Pointer pointer in pointersToLoopOver)
                    ProcessPointer(pointer);
            }
        }
    }