Magic Leap: Raycasting
/So you wanted to raycast huh?
I got just the thing for you. How’s about instantiating something on a raycast and bumper press? Sound good? Awesome. Let’s get started.
The first thing that’s probably decently good to know is that raycasting is one of the trés things that the ML1 animal uses to get information from the environment. We can do this by using Unity’s Physics.Raycast or we can do this using MLWorldRays (I’ll explain this one later).
Somewhat and not-so-somewhat, this guide is based off of Magic Leap’s 6-Dof tutorial, peppered with some of that not-so-somewhat.
First off, I’ll show you how to use Physics.Raycast.
Open unity and get it all ready to make a Magic Leap project. This means going through the whole New Project > Magic Leap > blah blah continued, Import Custom package > C:\Users\User\MagicLeap\tools\unity\v0.17.0, Build Settings > Lumin SDK location > change platform etc etc.
Oh and ENABLE ZERO ITERATION. Magic Leap > Enable Zero Iteration. Open up the Magic Leap Simulator and load a virtual room (downloaded from Magic Leap Package Manager).
Unity is all set up and done. Awesome. Almost.
In Player Settings (under Build Settings) add WorldReconstruction. That enables us to use MLAnythingThatRelatesToReconstructingTheWorld (like mesh and raycast).
Let's make a prefab cube next. This will be the object to instantiate. GameObject > 3D Object > Cube.
Next, make a CubeMaterial, right click in the Assets tab > create > material. Name it something like CubeMaterial. Selecting CubeMaterial, In the inspector tab, there's a wee little Albedo color block thingy so click it, and make it whatever color you want (like FF0000). So far your little raycast project should look a little like this:
Select the Cube in the Hierarchy, drag the CubeMaterial to the under the Inspector Tab and drop it into the Element 0 box (that said Default-Material). Your cube is now red. Awesome. This is how you change an object's material. Also change the scale of the Cube to X 0.2, Y 0.2, and Z 0.2. This changes the size of your cube to itty bitty.
Next, make the cube a prefab by dragging the cube from the Hierarchy to the Assets folder. In the Hierarchy the Cube text is blue signalling that the Cube is a Prefab and we can now delete it from the Hierarchy without deleting the whole thing from our project.
Now we want to create the little pointer thingy that points at stuff.
GameObject > Create Empty > Rename to Pointer
Select Pointer > Create Empty Child > Rename to PointerCube
Set the scale of the cube to X 0.001, Y 0.001, Z 2 (what it looks like)
Set the transform in the z direction of the PointerCube to 1.07. (where it is)
In the Inspector Tab, set the Layer to Ignore Raycast (if this doesn’t happen the raycast will hit the pointer and instantiate the cube there).
Next next, and a very important next, is adding the MLSpatialMapper to our project. Search for the thing in the Asset/Project search bar thingy then drag and drop it into the Hierarchy. You can leave it’s mesh as the default Wireframe or change it to something invisible as I’ve shown in the previous meshing post.
Search for controller the same way we searched MLSpatialMapper, find the prefab, drag and drop that thingy into the Hierarchy. This will make our lives much easier in the simulator.
And now the fun part, the C# script. Same way we made the CubeMaterial except this time we select new C# script. Name it something (like CubeRaycast), and drag it onto Pointer in the Hierarchy tab. Double click to edit it.
SCRIPTING!! Right away we want to add under using UnityEngine; using UnityEngine.XR.MagicLeap;
This is the Magic Leap namespace, what gives you access to all things Magic Leap. If the script you’re writing doesn’t need any of the MLStuff.DoMLstuff you don’t have to have in there, otherwise, YOU MUST HAVE IT for your script to work.
Unity raycasts work a little something like this:
if (Physics.Raycast(origin,direction, out hit, maxDistance)) { //do something here. }
This is the starting point of our little pointer thingy.
So now we gotta declare variables n controller stuff.
public class CubeRaycast : MonoBehaviour { public GameObject _cube; private MLInputController _controller;
In our Start() function we have to start collecting information.
MLInput.Start() // to start receiving input from the controller MLInput.OnControllerButtonDown += OnButtonDown; // a listener function that listens for the button input. _controller = MLInput.GetController(MLInput.Hand.left); //left or right it doesn’t really matter
In the Update() function we have to get the controller position and rotation.
transform.position = _controller.Position // a Vector3 quantity transform.rotation = _controller.Orientation // a Quaternion quantity
Pay attention to the _controller.Orientation.
SOO…
The real meat of things, the OnButtonDown() function.
void OnButtonDown(byte controller_id, MLInputControllerButton button) { }
When using _controller.Position and _controller.Orientation, it’s not explicitly stated that if you wanted to typically Physics.Raycast from the controller that you need to turn the _controller.Orientation into a Vector 3 stored in a raycast result.
To do this it would be something like
If (Physics.Raycast(_controller.Position, transform.forward, out hit)) { Debug.DrawRay(hit.point, Quaternion.Euler(hit.normal)); //in scene mode (Unity), draw a ray whose origin is hit.point and direction is up (the normal)[[thank you Raghav]] }
Back to the void .
Add our raycast as an ‘if’ statement, and within that ‘if’ statement we’ll put another ‘if’ statement for the bumper. Cause I heard you liked ‘if’ statements.
void OnButtonDown(byte controller_id, MLInputControllerButton button) { if (Physics.Raycast(_controller.Position, transform.forward, out hit)) { if (button == MLInputControllerButton.Bumper) { GameObject cube = Instantiate (_cube, hit.point, Quaternion.Euler(hit.normal)); } } }
Remember quaternion, vector3 thingy I mentioned? It’s here.
Ctrl + s. Nintendo.
In the hierarchy, select your Pointer and notice in the inspector tab you have your public variables showing. Your _cube GameObject needs an object, so drag the prefab cube we made earlier into that little slot.
Hit the play button and lets see if it works.
Magic.
As usual, there’s always a better way to do this. Probably.
Here’s the full code: