Using Android Wear to control Google Cardboard Unity VR
Using a VR headset, even one as simple as Google Cardboard, can be mind-blowing. Nevertheless it is the little things that can also be disconcerting. For example looking down and seeing you have no arms, despite the fact they still very much feel as though they exist.
I’m convinced that VR experiences are going to transform not just games, but interaction with computers in general, and I’ve been experimenting with some ideas I have about how to create truly useful VR experiences.
As I was working to implement one of my ideas, it occurred to me that I might be able to use the orientation sensors in the Android Wear device I was wearing. Why not use them as input into the VR experience I was creating? What if I could bring part of my body from the real world into the VR world? How about an arm?
I decided to try to find out, and this was the answer:
The experience is nowhere near good enough for games. But I don’t care about games. I want to create genuinely useful VR experiences for interacting with computers in general, and I think this is good enough. I can point to objects, and have them light up. I can wear smart watches on both wrists (because I really am that cool) and have two arms available in the VR world.
By tapping and swiping on the wearable screens I can activate in-world functionality, without being taken out of it. It sure beats sliding a magnet on the side of my face, because it is my arm I am seeing moving in the virtual world.
In the rest of this article I’m going to describe some of technical challenges behind implementing this, how I overcame them, and some of the resources I used on the way.
This is part of my workspace: Android Studio on the left, Unity on the top-right and MonoDevelop on the bottom-left:
I had many reference browser windows open on other screens (obviously), and creating this solution required me being very comfortable in Android, Java and C#. I’m relatively new to Unity.
Creating a Unity Java Plugin by overriding the Google Cardboard Plugin
The Unity Android Plugin documentation describes how you can create plugins by extending the UnityPlayerActivity Java class, and I experimented with this a little. I created an Android Library using Android Studio, and implemented my own UnityPlayerActivity derived class.
After a little hassle, I discovered that Unity now supports the “aar” files generated when compiling libraries in Android Studio, although I found the documentation a little out of date on the matter in places. It was simply a question of copying my generated “aar” file into Unity under Assets|Plugins|Android
When it came to a Google Cardboard Unity project, what I discovered though, is that Google had got there first. They had created their own UnityPlayerActivity called GoogleUnityActivity. What I needed to do was override Google’s override:
I included Google’s unity classes as dependencies in my library project:
Once I’d copied the aar file into the Unity Android Plugins folder and ran the test app, I was delighted to see my activity say “Cooey” in the log.
Receiving the watch’s orientation to the phone
The next step was to receive Android Wear Messages from Android Wear on the watch, containing orientation messages.
I recreated my project, this time including support for Android Wear:
I made the Unity activity I’d created do a little more than say “Cooey”.
First I used the Capabilities mechanism to tell other Android Wear devices that this device (the phone) was interested in arm orientation messages:
… and I set it up to receive Android Wear messages and pass them over to Unity using UnitySendMessage:
Sending the watch’s orientation to the phone
This was simply a question of looking out for Android Wear nodes that supported the right capability, listening for orientation sensor changes, and sending Android Wear messages to the right node. This is the watch code:
I did discover that some wearables don’t support the required sensors, although I imagine more modern ones will.
Using the watch’s orientation to animate a block on the screen
Inside Unity I created a cube which tweaked into a rectangle, and made it a child of the CardboardMain’s camera, so that it moved when I moved:
See the “Script” field on the bottom right-hand side? I have a script called “WristController” that is attached to the “wrist” (white blob). This is where I receive orientation messages sent from the watch, via the UnityPlayerActivity derived Java class I’d created.
I started off simply assigning the received orientation to the block’s orientation by assigning to transform.eulerAngles
This worked, but was super-jerky. I went searching and discovered Lerps and Slerps for smoothly moving from one rotation to another. My updated code:
Animating an arm instead of a block
I was pleased to be smoothly animating a block, but my arm doesn’t quite look like that. It is more armish. I went looking for a model of an arm that I could import and use instead. I found a YouTube Unity video called ADDING ARMS by Asbjørn Thirslund, in which he explains how to to import and use a free arms model by eafg.
It was simply a question of sizing and positioning the arms properly as a child of the Cardboard main camera, and then adding the script I’d used to animate the block.
I also removed the right-hand arm, since it looked a little weird to have a zombie arm doing nothing.
The ArmController script you see in this screen capture has the same contents as the WristController I’d used to move the block.
There is enough of a lag to make this technique impractical for games, but not enough to make it impractical for the kinds of non-game experiences I have in mind.
I’d also need to add calibration, since the watch may be pointing in any direction initially – if I assume it always starts straight out, that would be good enough. Detecting where the arm is pointing shouldn’t be too hard, since the cardboard code already does gaze detection – so many possibilities, but so little time for side-projects such as this!
This has been a fun interlude on my way to creating what I hope to be a genuinely useful VR experience based around browsing books your friends have read … more on that later.