3D - Avatar Interacting with Objects (Difficulty: 4)
-
Introduction
In many games, the player controls an avatar to interact with other objects in the scene, such as colliding with walls or picking up reward items. In this tutorial, we will walk through 3 methods you can apply in your next game: ray cast, bounding box and distance measurement.
Step 1 - Remix the Starting Project
A starting project has been shared here:
https://play.creaticode.com/projects/5c3039e3635591a0e3388dde
Please remix this project as your own.
This project contains code blocks to create a running avatar for the player, and add some other objects: a ramp leading up to a platform, a crown and a coin to pick up, and a circular goal area.
Step 2 - Add Ray-cast Based Collision with the Platform
Currently, the avatar can not walk on the ramp or the platform yet. Instead, it will walk through the ramp.
&nsp;
We can fix that by turning on raycast-based collision with the platform.
In the “avatar” sprite, insert the following block (you can find this block by searching for “collision”):
Note that you need to set the parameters correctly:- It is a “Blocking” collision, which means the avatar will stop when there is a collision;
- The collision target is the "platform’ sprite, so the avatar will only collide with objects created in the platform sprite, which are the ramp and the platform.
- The precision is “Bottom Front”, so 2 sensing rays are added to the avatar, one pointing down to the ground and one pointing forward at his feet.
- The “debug” option is “Yes”, so we can see the rays.
When we run the project again, we do see the sensing rays and a yellow box (the “collider”) that represents the avatars body. However, there is a problem that the yellow box is centered at the bottom of the avatar, and the sensing rays are below the ground. That is because the position of all avatars and models is represented by their bottom points, not their centers.To fix this issue, we just need to add a “z offset” to move up the yellow box by half of the avatar’s height, which is 100/2 = 50.
Now we see the yellow collider box and the 2 sensing rays are both correctly positioned:
Step 3 - Run up the Ramp
Now we get this when we try to make the avatar run up the ramp.
The good news is the collision detection is working, so the avatar “knows” about the ramp, and doesn’t go through it anymore. The bad news is that the avatar gets stuck. That’s because the forward ray is reporting that we are blocked forward.To fix this issue, we need to change the “precision” to “low”, which will install 6 rays in all 6 directions, but raise the forward ray higher to the body center.
Now our avatar is able to run up the ramp. That’s because the forward ray has a larger distance to the ramp, so it does not report collision with the ramp any more.
Note that if you use the “move to xyz position” block to move the avatar, it would still go through the ramp. So you have to use the “set speed” block in combination with the raycast-based collision.
Step 4 - Fix the Animation and Movement
At this point, the avatar does not stop when it runs on the ramp, and it is showing the “jump in air” animation all the time.
That is because the program still uses the “z position” of the avatar to test if it is jumping or not. Since we have turned on the raycast-based collision, we can check if the avatar is being blocked by the ground or some obstacle instead.
In the avatar sprite, scroll down the “handle keys” block’s definition, and make this change, so that we will show the jump animation when the avatar is not blocked by anything below its feet.
Now the animation is fixed, and the avatar can stop on the ramp:
Step 5 - Collide with the Crown
Now let’s move on to our next task, which is to make the avatar collide with the crown on the platform. There are a few ways to do this. We will first try to use the same raycast-based method.
We need to make 2 code changes:
First, in the avatar sprite, we need to turn on a “Non-Blocking” collision between the avatar and the “objects” sprite. It is set to non-blocking because we do not want the avatar to be stopped by the crown.
Next, in the “objects” sprite, we need to handle the collision event against the “avatar” sprite", using the “when colliding” block. Since there are 2 objects created in this sprite, the object that’s actually colliding with the avatar is automatically selected as the active sprite object. We can verify that by printing the name of the sprite object.
Now when the avatar runs into the crown, we see a message printed in the console panel. It says the “objects” sprite is printing a message “a”, which is the name of the object colliding with the avatar.
Step 6 - Remove the Crown
Now we know the name of the object that’s colliding with the avatar is “a”, we can easily remove it, as if the avatar has “picked up” the crown.
We don’t even need to specify the name of the object to be removed, since the crown is already selected as the active sprite object when the collision occurs. Now the crown will be removed when the avatar touches it:
Step 7 - Collect the Coin?
Now if we try to move the avatar to collect the coin, we find that it does not work:
The reason is that we are using a raycast-based method, so the avatar would only collide with an object if it is touched by the rays. In this case, the coin is above the forward ray, so it would not trigger the collision event. It would not collide with the ray that’s pointing up, because the avatar is not rising up, so it is not moving up into the coin. If we make the avatar jump, then it will trigger the collision event:
Obviously, this is not a great solution. We need to make the avatar pick up the coin whenever he touches the coin. We can switch to another method next.
Step 8 - Show the Bounding Box
Every object in the 3D scene has an invisible “bounding box”, which is a box that wraps the entire object. To display the bounding box of an object, you can add the “show bounding box” block.
In the “avatar” sprite, let’s remove the collision with the “objects” sprite, and hide the collision rays. Then let’s add the “show bounding box” block:
You will get this white box frame around the avatar:
Similarly, in the “objects” sprite, let’s remove the “when colliding” event handler, and show the bounding boxes of the crown and the coin:
You can see the bounding boxes for these 2 objects now:
Step 9 - Detect Bounding Box Overlap
It is much faster to calculate if 2 bounding boxes are overlapping, compared to checking if the exact body shapes of the 2 objects are overlapping. We can ask the game engine to broadcast a message whenever the avatar’s bounding box is overlapping with any objects from the “objects” sprite.
First, in the “objects” sprite, let’s create a new message named “touching object”, and store the additional information in the variable “info” (the variable has already been created for you). We can simply print out the content of “info” for now:
Next, in the “avatar” sprite, add the “broadcast” block to broadcast the “touching object” message when the avatar overlaps with objects from the “objects” sprite. Note that “p” is the name of the avatar object, and “ALLOBJECTS” is a special parameter that means all objects in the “objects” sprite.
Now when the avatar touches the crown, the “info” variable will contain the following information: “p,a”. That means object “p” is touching object “a”.
Step 10 - Remove the Touched Object
Now we have found another way to know that the crown object named “a” is touched by the avatar. So we can just remove that object using its name in the “objects” sprite:
We are using the “part of” block to split the “info” variable into 2 parts by the comma separator, and the second part is the name “a”. So we can use that name to remove the object named “a”. When the avatar is touching the coin, the name will be “c”.
Note that the coin is successfully detected, because the avatar’s bounding box will touch the coin’s bounding box. This makes it a better solution for picking up small objects than using the raycast method.
Step 11 - Detect the Goal Area Using Bounding Box
For our last task, we will try to detect when the avatar has arrived at the circular goal area. Since the goal area is a thin/flat cylinder, it won’t be easily detected by the forward ray sensor. So let’s try to use the bounding box overlap method.
First, in the “avatar” sprite, add a new message handler for “touching goal”. And when we receive this message, we will make the avatar stop. We can stop all other code blocks, and also set the avatar’s forward speed to zero. We will them make the avatar show the “Victory” animation. Note that the starting project already added the “Victory” animation to the avatar for you.
Secondly, still in the “avatar” sprite, broadcast the “touching goal” message when the avatar’s bounding box is overlapping the bounding box of the goal.
Now when the avatar touches the bounding box of the goal area, it will stop right there:
Step 12 - About Distance between Objects
There is are some small issues when we use the bounding box to check for the goal area. When avatar would stop right at the edge of the goal area, not its center. Also, since the bounding box is bigger than the goal itself, the avatar may get stuck at the corner of the bounding box:
In addition, it is still costly to calculate if the 2 bounding boxes are overlapping on every animation frame.To solve these problems, we can try another method: we can simply calculate the distance between the avatar and the goal, and if that distance is smaller than a threshold, we claim the avatar has arrived at the goal area.
Note that when we measure the distance between 2 objects, we are referring to the distance between their positions. For simple shapes like the cylinder, its position is the position of its body center. For a model or avatar, its position is the position of its bottom center.
For example, in this picture below, the distance between the goal area and the avatar is the distance between the 2 red dots:
Step 13 - Detect the Goal Area Using Distance
To use the distance measurement for checking if the avatar has arrived at the goal, we need to remove the block for checking bounding box, and replace it with the distance detection block:
Note that we are checking for “3D” distance, which considers the X/Y/Z positions of the 2 objects. If we choose “2D” instead, then the Z position would be ignored. Then even if the avatar is standing right below the goal area on the ground, its 2D distance to the goal area can still be very small.We are using a threshold of 30, so the game engine would automatically broadcast the message when the distance is shorter than 30.
Now the “touching goal” message would be sent out only when the avatar is much closer to the center of the goal area.
Step 14 - Summary
In summary, you should decide which of the 3 methods is suitable depending on the problem you are solving:
- Raycast-based method is great for detecting obstacles that will block the avatar’s mvoement, such as walls, platforms, ramps and stairs. It won’t work when the obstacle is too small and could not be detected by the sensor rays.
- For smaller objects, such as coins or equipment, we can check if the avatar’s bounding box is touching the bounding box of those objects.
- We can also use distance-based methods if we just need to know that the avatar is close to another object. This method is the fastest, since it is much simpler to calculate the distance between 2 points compared to calculating if 2 bounding boxes are overlapping or if the ray is touching another object.
Note that if the player is driving a car or flying an airplane, instead of controlling an avatar, you can still use the same methods described here.
-