XRHapticsManagerPro

About

XRHapticsManagerPro solves the limitations of OpenXR's ability to send haptic feedback. It enables users to process overlapping impulses, add to, multiply to, and override haptic feedback, and supports Curves and custom types. If your project needs to process different feedback at the same time or needs support for non-float and curve feedback, XRHapticsManagerPro will solve those needs. Otherwise, I recommend using OpenXR's base functionality.

The Need

I discovered the need for this system while working as a ULA for my Virtual Worlds class. Through my own projects, I quickly discovered that the base functionality for sending haptic feedback in OpenXR was incredibly limited. When I met with one of the teams in the class that wanted to recreate a humming lightsaber for their VR game, I knew that it wouldn't be possible without a better system, so I took this opportunity to make a useful solution that can extend to other projects.

OpenXR allows users to send haptic impulses to a controller with a strength, and intensity. But that's it! Sending one impulse after another completely overrides and cancels the other haptic impulse.

Development

I began my development process by evaluating the needs of the team I was creating this for, and started coming up with ideas for solutions. I talked with my peers, and decided to use a collection of data containing values and durations for each controller,  processing and applying an impulse for X seconds, every X seconds, using OpenXR's base SendHapticImpulse() function. I started by making a ScriptableObject class to contain the haptic data and a simple manager that would send an impulse to the controller based on the data. Not long after, I decided that I wanted to include support for curves to allow teams to get creative, and remembered that I can use generic classes to keep both floats and curves in the same collection. This is where my first challenge began.

My first challenge was to create data for the haptic system to read, and write to/track the duration of each impulse so it will only last as long as it is asked to. I am using ScriptableObjects to let designers create haptic impulses because it is convenient for designers to use throughout the project, but information cannot be written to the ScriptableObject, because there may be several uses of the same data at the same time. This means that I cannot store the duration of an impulse on the ScriptableObject, and need to find a new way to do it.

Rather than creating a list of HapticData and durations, I created a new class called HapticImpulse that keeps a reference to the HapticData, and stores the duration. HapticImpulse also subscribes to an Action on the XRHapticsManager to iterate upon the duration, and invokes an event when reaching max duration- removing itself from the XRHapticsManager.

I needed functionality for pulses and sequences, to allow for creative uses of haptics. This meant that I needed to incorporate support for curves. I started researching generic types as I had not used them before, but wanted to use them so that the system operates with all types of data in the same way, and as a bonus, I decided that allowing support for custom types could be a feature. It was tricky, but I transformed HapticData and HapticImpulse to be generically typed, and created subclasses for each typed version. This adjustment required that HapticImpulse needs a function to return a float from the type that it stores.

Features

The most up to date version of XRHapticsManagerPro supports:

-Sending Impulses as floats or curves.

-Additive impulses, multiplicative impulses, and override impulses that ignore other impulses.

-The ability to create and send any type other than float/curve as an impulse.

-Duration, adding more control to custom types.

What I Learned

Developing this system gave me my first experiences using generic classes and great practice and creating data structures, but most importantly- planning and documenting early.

I made a mistake by not starting with a written plan or document. I had a solid idea in my head, but nothing down on paper that I could refer to later or share with others. By not planning appropriately, I jumped into this project and quite immediately faced two large challenges that I could have worked at before I had started. Also, there were occasions that I had to prioritize other work, and upon coming back I had to relearn what what my classes did exactly, and then remember what the next step was. 

I made sure to document my code with comments. When I picked up from where I left off I could easily read through and remember how things worked or why they were made that way. I had learned from working with my team on Grimmoire to leave to do comments on my code, which I implemented into this project. These were incredibly helpful as I could look to the top of each file, and remember what the next step was.

For my next project, I want to write out my ideas on paper or a whiteboard before I jump into development. This way, I can work out conceptual problems before I begin, I can document my process better, and I can look back to my drawing for how to setup.

Future Plans

I am currently polishing up the entire system to release a preview version. I'm making sure that everything is well-documented, the code is nice and neat, and no bugs have slipped through the cracks.

My next two goals are to expand upon the removal function in the manager, as it has limited functionality. My other goal is to publish this the system on the Unity Asset Store, to hopefully get more eyes on it, help more people, and iterate to make the system better.

For the further future, I have planned to implement operation orders and priority layers, to give users more control over the haptic feedback they are sending. I had initially planned to add these features for the class, but it was a niche feature that would be unlikely to be used, and I wanted to get the system into their hands before their timeline was up- so I cut it from development.