Skip to content

Instantly share code, notes, and snippets.

Created October 4, 2016 14:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/ac8c76641f7b9b2608de50aae262644e to your computer and use it in GitHub Desktop.
Save anonymous/ac8c76641f7b9b2608de50aae262644e to your computer and use it in GitHub Desktop.
Unity Daydream Technical Preview Controller Bridge Code!
#if UNITY_ANDROID
using UnityEngine;
using System;
using System.Runtime.InteropServices;
using Assets.GoogleVR.Scripts.Controller.Internal.Wrappers;
/// @cond
namespace Gvr.Internal
{
/// Controller Provider that uses the native GVR C API to communicate with controllers
/// via Google VR Services on Android.
class AndroidJavaControllerProvider : IControllerProvider
{
private const string UNITY_PLAYER_CLASS = "com.unity3d.player.UnityPlayer";
private AndroidJavaObject androidContext;
private ControllerListenerWrapper controllerListener;
private ControllerManagerWrapper controllerManager;
private ControllerWrapper controller;
private bool error;
private String errorDetails;
private ControllerState state = new ControllerState();
internal AndroidJavaControllerProvider()
{
Debug.Log("Initializing GVR Controller API.");
// Get a hold of the activity, context and class loader.
AndroidJavaObject activity = GetActivity();
if (activity == null)
{
error = true;
errorDetails = "Failed to get Activity from Unity Player.";
return;
}
androidContext = GetApplicationContext(activity);
if (androidContext == null)
{
error = true;
errorDetails = "Failed to get Android application context from Activity.";
return;
}
Debug.Log("Creating and initializing GVR API controller object.");
try
{
controllerListener = new ControllerListenerWrapper();
controllerManager = new ControllerManagerWrapper(androidContext, controllerListener);
controller = controllerManager.GetController();
controller.SetEventListener(controllerListener);
}
catch(AndroidJavaException e)
{
Debug.LogError("Error creating/initializing GVR Controller API.");
error = true;
errorDetails = "Failed to initialize GVR Controller API.";
return;
}
Debug.Log("GVR API successfully initialized. Now resuming it.");
controllerManager.Start();
Debug.Log("GVR API resumed.");
}
public void ReadState(ControllerState outState)
{
if (error)
{
outState.connectionState = GvrConnectionState.Error;
outState.errorDetails = errorDetails;
return;
}
controller.Update();
outState.connectionState = controllerListener.ConnectionState;
// Note: for accelerometer, gyro and orientation, we have to convert from the space used by
// the GVR API to Unity space. They are different.
// GVR API: X = right, Y = up, Z = back, right-handed.
// Unity: X = right, Y = up, Z = forward, left-handed
//
// So for orientation and gyro, we must invert the signs of X, Y, and Z due to chiral
// conversion, and then must flip Z because of the difference in the Z axis direction.
// So, in the end, the conversion is: -x, -y, z.
//
// For the accelerometer, there is no chirality conversion because it doesn't express
// a rotation. But we still need to flip Z.
//outState.accel = new Vector3(state.accel.x, state.accel.y, -state.accel.z);
//outState.gyro = new Vector3(-state.gyro.x, -state.gyro.y, state.gyro.z);
outState.orientation = new Quaternion(
-controller.Orientation.x, -controller.Orientation.y, controller.Orientation.z, controller.Orientation.w);
outState.touchPos = controller.Touch;
outState.isTouching = controller.IsTouching;
outState.touchDown = controller.IsTouching && !state.isTouching;
outState.touchUp = !controller.IsTouching && state.isTouching;
outState.appButtonState = controller.AppButtonState;
outState.appButtonDown = controller.AppButtonState && !state.appButtonState;
outState.appButtonUp = !controller.AppButtonState && state.appButtonState;
outState.clickButtonState = controller.ClickButtonState;
outState.clickButtonDown = controller.ClickButtonState && !state.clickButtonState;
outState.clickButtonUp = !controller.ClickButtonState && state.clickButtonState;
outState.recentering = false;
outState.recentered = controllerListener.Recentered;
state.CopyFrom(outState);
}
public void OnPause()
{
controllerManager.Stop();
}
public void OnResume()
{
controllerManager.Start();
}
private GvrConnectionState ConvertConnectionState(int connectionState)
{
return (GvrConnectionState)connectionState;
}
private static AndroidJavaObject GetActivity()
{
AndroidJavaClass jc = new AndroidJavaClass(UNITY_PLAYER_CLASS);
if (jc == null)
{
Debug.LogErrorFormat("Failed to get Unity Player class, {0}", UNITY_PLAYER_CLASS);
return null;
}
AndroidJavaObject activity = jc.GetStatic<AndroidJavaObject>("currentActivity");
if (activity == null)
{
Debug.LogError("Failed to obtain Android Activity from Unity Player class.");
return null;
}
return activity;
}
private static AndroidJavaObject GetApplicationContext(AndroidJavaObject activity)
{
AndroidJavaObject context = activity.Call<AndroidJavaObject>("getApplicationContext");
if (context == null)
{
Debug.LogErrorFormat("Failed to get application context from Activity.");
return null;
}
return context;
}
private static AndroidJavaObject GetClassLoaderFromActivity(AndroidJavaObject activity)
{
AndroidJavaObject result = activity.Call<AndroidJavaObject>("getClassLoader");
if (result == null)
{
Debug.LogErrorFormat("Failed to get class loader from Activity.");
return null;
}
return result;
}
}
}
/// @endcond
#endif
package com.company.controllerlistener;
import com.google.vr.sdk.controller.Controller;
import com.google.vr.sdk.controller.ControllerManager;
public class ControllerListener extends Controller.EventListener implements ControllerManager.EventListener {
public int apiStatus = 0;
public int connectionState = 0;
private boolean recentered = false;
private boolean updated = false;
@Override
public void onApiStatusChanged(int i) {
apiStatus = i;
}
@Override
public void onRecentered() {
recentered = true;
}
@Override
public void onConnectionStateChanged(int state) {
connectionState = state;
}
@Override
public void onUpdate()
{
updated = true;
}
public boolean getRecentered()
{
if(recentered)
{
recentered = false;
return true;
}
return false;
}
public boolean getUpdated()
{
if(updated)
{
updated = false;
return true;
}
return false;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Assets.GoogleVR.Scripts.Controller.Internal.Wrappers
{
class ControllerListenerWrapper
{
public enum ApiStatusValue
{
OK = 0,
ERROR_UNSUPPORTED = 1,
ERROR_NOT_AUTHORIZED = 2,
ERROR_UNAVAILABLE = 3,
ERROR_SERVICE_OBSOLETE = 4,
ERROR_CLIENT_OBSOLETE = 5,
ERROR_MALFUNCTION = 6
}
private AndroidJavaObject javaObject;
public ControllerListenerWrapper()
{
javaObject = new AndroidJavaObject("com.company.controllerlistener.ControllerListener");
}
public AndroidJavaObject GetJavaObject()
{
return javaObject;
}
public ApiStatusValue ApiStatus
{
get { return (ApiStatusValue)javaObject.Get<int>("apiStatus"); }
}
public GvrConnectionState ConnectionState
{
get { return (GvrConnectionState)javaObject.Get<int>("connectionState"); }
}
public bool Recentered
{
get { return javaObject.Call<bool>("getRecentered"); }
}
public bool Updated
{
get { return javaObject.Call<bool>("getUpdated"); }
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Assets.GoogleVR.Scripts.Controller.Internal.Wrappers
{
class ControllerManagerWrapper
{
private AndroidJavaObject javaObject;
public ControllerManagerWrapper(AndroidJavaObject context, ControllerListenerWrapper listener)
{
javaObject = new AndroidJavaObject("com.google.vr.sdk.controller.ControllerManager", context, listener.GetJavaObject());
}
public void Start()
{
javaObject.Call("start");
}
public void Stop()
{
javaObject.Call("stop");
}
public ControllerWrapper GetController()
{
AndroidJavaObject controllerObject = javaObject.Call<AndroidJavaObject>("getController");
if(controllerObject != null)
{
return new ControllerWrapper(controllerObject);
}
return null;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Assets.GoogleVR.Scripts.Controller.Internal.Wrappers
{
class ControllerWrapper
{
private AndroidJavaObject javaObject;
public ControllerWrapper(AndroidJavaObject javaObject)
{
this.javaObject = javaObject;
}
public AndroidJavaObject GetJavaObject()
{
return javaObject;
}
public void SetEventListener(ControllerListenerWrapper listener)
{
javaObject.Call("setEventListener", listener.GetJavaObject());
}
public long Timestamp
{
get { return javaObject.Get<long>("timestamp"); }
}
public Quaternion Orientation
{
get
{
AndroidJavaObject orientation =
javaObject.Get<AndroidJavaObject>("orientation");
return new Quaternion(
orientation.Get<float>("x"),
orientation.Get<float>("y"),
orientation.Get<float>("z"),
orientation.Get<float>("w"));
}
}
public bool IsTouching
{
get { return javaObject.Get<bool>("isTouching"); }
}
public Vector2 Touch
{
get
{
AndroidJavaObject pointf =
javaObject.Get<AndroidJavaObject>("touch");
return new Vector2(
pointf.Get<float>("x"),
pointf.Get<float>("y"));
}
}
public bool ClickButtonState
{
get { return javaObject.Get<bool>("clickButtonState"); }
}
public bool AppButtonState
{
get { return javaObject.Get<bool>("appButtonState"); }
}
public bool HomeButtonState
{
get { return javaObject.Get<bool>("homeButtonState"); }
}
public bool VolumeUpButtonState
{
get { return javaObject.Get<bool>("volumeUpButtonState"); }
}
public bool VolumeDownButtonState
{
get { return javaObject.Get<bool>("volumeDownButtonState"); }
}
public void Update()
{
javaObject.Call("update");
}
}
}
@DashW
Copy link

DashW commented Oct 5, 2016

Be warned, this approach makes a lot of runtime allocations and could result in a lot of garbage collections. Seems to be an issue with the Unity Java Interop layer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment