Hi everyone and welcome to our blog! We are NormalVR, your neighborhood virtual reality shop.
As we delve deeper into VR, we occasionally come across obstacles and opportunities that we think are worth sharing with the VR community. We hope to write about how we navigate the unique set of problems VR presents developers and users. Today, we want to talk about something seemingly basic: throwing.
Throwing is a skill that many of us learn at an early age. It becomes intuitive and (once we reach adulthood) is something that feels inexplicably natural. We become hyper-aware of the physical sensations associated with it. As we waded into VR we noticed that this basic human ability wasn’t being translated well; throwing wasn’t consistent and didn’t feel intuitive. We won’t outline all of our failed experiments, but rather how we saw the problem and our proposed solution.
When you are throwing in VR there are many variables that need to be considered. If there is variability that’s introduced from your system, rather than the variability of your users, then throwing is going to feel frustrating and hinder your ability to develop good muscle memory for specific throws in the game. You can spend all day tweaking mass and drag on your RigidBodies in Unity to get something that feels good, but that won’t address the issue of consistency. Addressing this problem is especially crucial if throwing with accuracy is part of your core game mechanic.
SteamVR provides a velocity and angular velocity value for each controller. In our experience using these values it’s obvious that Steam spent a considerable amount of time to create something that feels natural and consistent. However, these values are only for the center of the controller.
If we attempt to use them for an object that is not attached to the center of the controller we get behavior that is consistent, but does not match the user’s expectations.
As is with most solutions, the simplest route proved to be the one that gave us the best results. Given that SteamVR provides high quality velocity and angular velocity values, ideally we should try to derive the values we want from them. Luckily, we can trick Unity’s RigidBodies into calculating velocity and angular velocity for any point around the controller. When implemented correctly objects behave the way we expect them to. 1
More importantly, the throws are consistent and accurate.
Brilliant! But how’s it gonna work?!
First, you’ll want to create an empty game object and rigid body that you can use to calculate the velocity at positions around the controller.
Whenever you get new controller positions from SteamVR, you’ll want to make sure you position this game object at the center of the controller. Some people like to do this in Update(), but we find the values are usually a frame behind. We generally like to register for SteamVR’s “new_poses” event and trigger our own method to update geometry. We won’t outline how to do that here, but we’ve made the code available at the end of this post.
Next you’re going to update the empty game object’s velocity and angular velocity using the values from Steam inside of your Update() method.
That’s it! Once the trigger is let go, you can use GetPointVelocity on the rigid body to sample the velocity at any point around the controller.
Grab the source on Github here
Hopefully this proved to be helpful! We’d love to hear how this worked for you, what you thought of the post, and so on. Let us know what you think in the comments. Until next time!
Max
Follow along on Twitter @MaxWeisel and @NormalVR for the latest updates.
Also published on Medium.
There’s also a helper function in the IVRSystem interface called ApplyTransform, which takes a TrackedDevicePose_t and a transform to remap the pose’s values to. The values you get through new_poses or SteamVR_Controller are adjusted to be at the origin of the tracked object (regardless of where the IMU actually lives in the hardware). We try to make the origins similar across all tracked devices (e.g. Vive vs Touch controllers) in order to preserve as much consistency as possible without requiring devs to make customization based on connected hardware. You can drop a SteamVR_RenderModel into your scene and use the ModelOverride drop-down to explore the origin and other attach points.
!!!!! That’s awesome! Just to make sure I understand this correctly. SteamVR derives velocity values for 0,0,0 on the tracked object which are the ones I use to then derive velocity values for an arbitrary point on the object. You’re saying I can cut out the middle step and go straight from the velocity/angularVelocity of the IMU to the arbitrary point I’m throwing from? Very very cool. I’ll have to do a follow up to this post. That cleans this code up significantly and removes the rigidbody dependency
Max.