Daydream VR
I figured it's a great time with Daydream's recent launch to talk about the unique experiences that one gets developing for that platform, and how to design/develop with one common source code base.
First, let's point out the obvious, if you're making a project where a large game engine makes sense, then stick with either Unity or Unreal.
Unity offers a very easy to work with system that can get you to a working product in little time with a lot of capabilities, but has the downside of only supporting what Unity supports. Unreal will give you the ability to have a middle layer between the ease of Unity and the ability to change drastic pieces of your engine as you have access to the source code Unreal is built with.
Both of these engines are fine choices for working across VR environments and if they solve your needs, then please use them; and only read the below for the fun and interesting work that you happily get to avoid.
However, if you want adventure, or desire complete control over the size of your app, have a code base that already is distributed elsewhere, or have some other reason for not wanting to use Unity or Unreal; then this post is for you.
Our goal here is to develop a unique in house product with an eye towards keeping it something that can seamlessly work cross-platform with a simple recompile command. So this gives us the question, what language works on iPhone, Android, PS4, and Windows/Linux? This question must be asked as it will dictate the next step, getting the development environment setup. We're going to be working in good ole' C++ and as this specific post is about Android, our development will be in Android Studio with the NDK.
Before we can discuss how to achieve one code base for everything, it is important to discuss the necessary differences. First, Daydream uniquely has a special controller that acts as a virtual laser pointer. There are no other solutions for input that the platform offers so we must support the controller. Both GearVR and Cardboard make use of the trackpad on the side and a simple OnTouch() is enough as the design typically will follow a gaze cursor that tracks with the middle of the view frustum.
However, this proves a notable challenge for our desire to support multiple platforms from the same code base. The challenge is having one input that follows center of frustum and another that follows the orientation of the controller. Imagine the case of a device that supports all three platforms. Samsung's next platform may well support Daydream, GearVR, and as any Android platform; Cardboard.
To make matters more interesting, the controller can run out of batteries. The current Google VR SDK has no method of determining the charge of the controller, so any Daydream device could wind up with no way to have input. This event could happen without warning, so I recommend turning to a FSM (Finite State Machine) to help us navigate this maze.
The first state in our FSM, is one in which the app is started without ever having connected to the controller. The assumption must be that the cursor follows the center of the frustum, this is the default initial case as until there is a controller, it is safe to assume GearVR or Cardboard model. This is a safe assumption as the final product does not launch unless the end user already has the controller connected, thus it is highly unlikely the end user would ever see the gaze cursor tracking the frustum center. During this state, we should attempt to connect to the controller. If the controller never connects, then there's no need to progress into a different state in our FSM.
The second state occurs when the controller connects. In this state, we need to continuously update the handedness of the controller, determine the orientation and position using inside out tracking from the orientation(s) of the headset and the controller. The cursor should now follow the forward unit vector of the controller.
The final state occurs when the controller runs out of batteries. There is no warning for when this happens. The connection is from bluetooth so there will be lag in knowing when this state occurs. My strategy here is to return the user to the home screen. Google's messaging handles the battery state and the application is automatically suspended and resumed when the user fixes the controller's connection (charges the controller). Thus they can continue the experience from their last point of departure. Upon resuming the app, we automatically transition back to the initial state and then the second state.
This strategy allows for a single code base to work across all three platforms without large changes. Most of the rest of the app can be written with the same code, and very platform specific work.
In a future post, I will share code that presents a simple app to demonstrate this strategy.
First, let's point out the obvious, if you're making a project where a large game engine makes sense, then stick with either Unity or Unreal.
Unity offers a very easy to work with system that can get you to a working product in little time with a lot of capabilities, but has the downside of only supporting what Unity supports. Unreal will give you the ability to have a middle layer between the ease of Unity and the ability to change drastic pieces of your engine as you have access to the source code Unreal is built with.
Both of these engines are fine choices for working across VR environments and if they solve your needs, then please use them; and only read the below for the fun and interesting work that you happily get to avoid.
However, if you want adventure, or desire complete control over the size of your app, have a code base that already is distributed elsewhere, or have some other reason for not wanting to use Unity or Unreal; then this post is for you.
Our goal here is to develop a unique in house product with an eye towards keeping it something that can seamlessly work cross-platform with a simple recompile command. So this gives us the question, what language works on iPhone, Android, PS4, and Windows/Linux? This question must be asked as it will dictate the next step, getting the development environment setup. We're going to be working in good ole' C++ and as this specific post is about Android, our development will be in Android Studio with the NDK.
Before we can discuss how to achieve one code base for everything, it is important to discuss the necessary differences. First, Daydream uniquely has a special controller that acts as a virtual laser pointer. There are no other solutions for input that the platform offers so we must support the controller. Both GearVR and Cardboard make use of the trackpad on the side and a simple OnTouch() is enough as the design typically will follow a gaze cursor that tracks with the middle of the view frustum.
However, this proves a notable challenge for our desire to support multiple platforms from the same code base. The challenge is having one input that follows center of frustum and another that follows the orientation of the controller. Imagine the case of a device that supports all three platforms. Samsung's next platform may well support Daydream, GearVR, and as any Android platform; Cardboard.
To make matters more interesting, the controller can run out of batteries. The current Google VR SDK has no method of determining the charge of the controller, so any Daydream device could wind up with no way to have input. This event could happen without warning, so I recommend turning to a FSM (Finite State Machine) to help us navigate this maze.
The first state in our FSM, is one in which the app is started without ever having connected to the controller. The assumption must be that the cursor follows the center of the frustum, this is the default initial case as until there is a controller, it is safe to assume GearVR or Cardboard model. This is a safe assumption as the final product does not launch unless the end user already has the controller connected, thus it is highly unlikely the end user would ever see the gaze cursor tracking the frustum center. During this state, we should attempt to connect to the controller. If the controller never connects, then there's no need to progress into a different state in our FSM.
The second state occurs when the controller connects. In this state, we need to continuously update the handedness of the controller, determine the orientation and position using inside out tracking from the orientation(s) of the headset and the controller. The cursor should now follow the forward unit vector of the controller.
The final state occurs when the controller runs out of batteries. There is no warning for when this happens. The connection is from bluetooth so there will be lag in knowing when this state occurs. My strategy here is to return the user to the home screen. Google's messaging handles the battery state and the application is automatically suspended and resumed when the user fixes the controller's connection (charges the controller). Thus they can continue the experience from their last point of departure. Upon resuming the app, we automatically transition back to the initial state and then the second state.
This strategy allows for a single code base to work across all three platforms without large changes. Most of the rest of the app can be written with the same code, and very platform specific work.
In a future post, I will share code that presents a simple app to demonstrate this strategy.
Comments
Post a Comment