In AR/VR Development
Godot VR Climbing - read the full article about VR tutorial, AR/VR Development and Augmented & Virtual Reality Solutions from Bastiaan Olij on Qualified.One
Youtube Blogger
Hi and welcome to another Godot VR tutorial video.
Today were going to look at a climbing mechanic.
Ive created this example project and well just look at how its put together. The link to the github project is in the description below. Let me know in the comments if you prefer this style of video.
The mechanic is pretty straight forward, the player can grab the hand holds and pull themselves upwards. The scene in Godot consists off a small section of a rock face, its only about 60 meters high and maybe 100 meters wide. Most of the rock face is created with a single asset that is used multiple times and simply rotated to create variations.
Standing on the rock face obscures enough of the surroundings that the player has no idea there isnt a whole mountain there.
This is a very simple scene that can be dressed up a lot further but it does show how you can obtain a really nice effect with very little assets.
We have a player sub scene here, well have a closer look at that in a minute.
Well start with the hand holds scattered along our path up the mountain.
We have a single scene for a hand hold that is just used a number of times.
Looking at this scene we see that is a very simple scene.
Ive just used two bars to make the hand hold, you can replace it with a nicer mesh or create variations.
At the root we have a static body, this allows us to interact with the hand hold. Ive put this static body into layer 2 to make detection easier.
We also need a collision shape for our static body and this is just a box that contains the hand hold.
We also have two spatial nodes that allow us to anchor our hands to when we grab the hand hold.
There are separate ones for each hand as their orientation is slightly different.
Just to demonstrate this Ill load in my hand mesh scene and we can see the hand anchored to one of these spatial points.
Looking at the script for this scene all we find here is a helper method that returns the global transform of the anchor for a given hand.
Looking at our player we find our standard ARVROrigin node at the root. Im using the OpenXR plugin here and have assigned its first person controller script to my root node.
The rest of the scene is setup with different subscenes for the hands hence Im not using the first person controller scene from the plugin.
We have a configuration node that provides easy access to the configuration of the OpenXR plugin.
We also have an ARVRCamera node, note that I have unticked layer 10 and added a mesh as a child that has been placed in this layer.
This results in our head casting a shadow without the head actually being visible to the player.
For now the head is just a capsule shape.
Next are our left and right hand scenes, we reuse the same scenes as we want the same logic to enable grabbing hand holds with either hand.
At the root of our hand scene is an ARVRController node.
Next is a spatial node on which we anchor our hand mesh.
This is the node well be moving to adjust the position of the hand mesh when we grab a hand hold.
Note that this node is moved into place so our hand mesh is in a natural position as our controller node is not centered on the hand.
Finally we have a subscene that is our hand detector node.
At the root here is an area node that is set to detect any object within the area that are in layer 2, so it will only detect our hand holds.
Looking at the script on our hand controller well find all the logic that allows us to grab the hand holds.
At the top weve got a variable that keeps track of which hand hold is nearest to us.
We have a variable that indicates whether we are holding on to that hand hold.
And we have a variable in which we store our original transform for our anchor. As well be moving this we need to remember this location.
Next we have a helper method that returns our is holding variable, well need this later on.
Then when our script is ready, and when our detector area detects hand holds entering or leaving our area we call a function that will determine our closest hand hold.
We can see here how our signals are setup.
We obtain an array with hand holds that are in our area, if this array is empty we clear our variable.
If we do have entries in our area we loop through this to find out which one is closest to us.
Looking at our process code we default our is holding to false and then check if we have a nearest hand hold.
If not we simply reset the transform to our original value and we assign our grab property on our hand mesh scene based on our grip value.
On controllers that support it, our grip value represents how much we are squeezing our controller.
The property on our hand mesh scene changes whether our hand is open or closed.
If we do have a hand hold were closest to we first obtain the transform for the correct anchor point from our hand held scene.
We want this transform in the local space of our controller node, to do this we multiply the inverse of our controllers global transform with the anchors transform.
This means that both our hand held anchor and our source anchor are in the same locality making it easy to work with them.
Now we check if our grip button is pressed.
If this is true we place our hand anchor using the transform we calculated from the hand holds anchor. This places our hand on that hand hold even if the physical location of our hand isnt exactly at this position.
We also set our grab property to 1 closing our hand so it looks like weve grabbed the hand hold.
Finally we set our is holding variable to true.
If our grip button is not pressed we are going to move our hand between our controllers position and the position of the hand hold based on the distance our controller is to our hand hold.
This gives the effect our hand hold acts like a magnet to our hand.
We simply calculate this distance, divide it by the radius of our detector to get a value between 0 and 1 and then interpolate between our hand hold anchor transform and our original transform.
We also assign our grab property based on our grab input on our controller.
All the code so far allows us to grab the hand holds we have placed around our scene but it doesnt yet move our player when the player moves their hands while holding a hand hold.
This logic is controlled by a script placed on a helper node called movement control.
As we want this logic to be performed after the logic on each hand, we want this further down in our node tree.
In our script we setup a few export variables so we know which nodes represent our hands and which node is our origin node.
Next we keep track off which hands are current holding hand holds and what their last position was. We also have a variable that tracks how fast were falling when were not holding any hand holds.
In our process function were going to check each hand.
First we obtain the is holding value from the script on our hand and then we compare it to the previous value.
If it has changed we simply assign the new value. When weve just grabbed a hand hold we dont know yet how much weve moved since grabbing it so we skip that bit.
If the value didnt change and we are holding our hand hold, we calculate our delta movement and add it to our delta variable and increase our count.
We do the same for our right hand as we did for our left hand.
Now we check if our hand holding count is bigger then zero.
If so we start by dividing our delta movement by the number of hands that is holding a hand hold.
If only one hand is holding a hand hold this count will be 1 and our delta will be just the movement of that hand.
If both hands are holding a hand hold our count will be two and our delta ends up being the average of the movement of both hands.
Now we simply subtract this delta movement from the global position of our origin node. This moves the player in the opposite direction of our hand movement.
To keep our hands locked on our hand holds we need to move the hand anchors for any hand holding a hand hold by adding our delta.
We also reset our fall velocity here.
If we are not holding any hand holds were going to apply our fall logic.
Here we simply increase our fall velocity by our gravity, and then apply our velocity to our Y position.
If our Y becomes 0 we hit the ground and stop falling. Im not detecting here if our player has falled to their death.
Finally for each hand that is holding a hand hold, we store their current positions so we can use them to calculate our deltas next frame.
And that is the whole solution to implement a climbing mechanic in VR.
Thank you for watching and especially a thank you to all my patrons and Youtube members who have stuck by me this year. If you want to support my work, please consider becoming a patron or a member.
You can follow me on twitter for more regular updates of what Im working on.
Please leave a like on the video if you enjoyed the content and let me know in the comment whether you enjoyed this format.
Until next time
Bastiaan Olij: Godot VR Climbing - AR/VR Development