Sometimes when we play games, we want to know the direction of objects that may be out of view or not visible. A radar is a UI element that makes it possible for this to happen. There are many ways in which a radar can appear, but in this recipe, we will make a directional radar that will take the form of an arrow and show the player the direction of the target.
If you are using Photoshop, you can easily create this by using the custom shape tool. You can select it by pressing U in the toolbox and Shift + U to cycle through the different shapes. Once the custom shape tool is selected, right-click to bring forth the shape selection panel. Select the arrow that you like. In this example, we selected the second arrow that was listed. Next, drag the arrow out onto the canvas while holding down the Shift key in order to constrain the proportions. Now we have a basic arrow icon.
Image
class every time we deal with the UI, we have to add the using UnityEngine.UI;
statement at the beginning of the script.private Image arrow;
player
and target
. Let's make these variables public
, since we should set them in the Inspector. So, let's add the following lines of code:public Transform player; public Transform target;
Start()
function, we have to get the value of our arrow
variable, like this:void Start () { arrow = GetComponent<Image>(); }
Update()
function we have to first calculate the projection of the position of the player on the floor, which is the xz
plane. In fact, most 3D games use this plan as the floor, and we want our radar to detect as if it is looking at the scene from the top view. We can do this very easily just by changing the y
component of the vector to zero (refer to the There's more... section to see how to project it on different planes). Therefore, we use this code:Vector3 playerProjection = new Vector3 (player.position.x, 0, player.position.z);
Vector3 targetProjection = new Vector3 (target.position.x, 0, target.position.z);
Vector3 playerDir = (new Vector3 (player.forward.x, 0, player.forward.z).normalized);
Vector3 targetDir = (targetProjection - playerProjection).normalized;
float angle = Mathf.Acos(Vector3.Dot (playerDir, targetDir))*Mathf.Rad2Deg
0
and 180
degrees, we still don't have enough information to know whether our radar has to turn clockwise or not. So, we have to distinguish two cases: whether the target is to the left or to the right of the player. If we perform the cross product between the two direction vectors and take the y
coordinate to check whether it is negative, we can distinguish the two cases. Finally, we assign a rotation along the z
axis to our arrow accordingly:if (Vector3.Cross (playerDir, targetDir).y < 0){ arrow.rectTransform.rotation = Quaternion.Euler (new Vector3 (0, 0, angle)); } else { arrow.rectTransform.rotation = Quaternion.Euler (new Vector3 (0, 0, -angle)); }
If you do not understand the concept of 3D geometry with vectors, this recipe may be a bit difficult to follow. However, the concepts covered in this section are not too difficult to work through, especially if we pay attention to the pictures.
We started by projecting both the player's position and the target's position, on the floor, like this:
Next, we projected the direction that the player is facing. We then called this projection vector playerDir
. Calculating the direction of the target respective to the player is simple, since we can just take the difference between the two vectors and normalize. We called this vector targetDir
.
Thus, we reduced the problem to just two dimensions. As a result, we only had to calculate the acute angle, theta, between playerDir
and targetDir
. It can be calculated as the arccosine of the dot product between the two vectors. Here is a diagram that shows the geometry of the player and the target:
Finally, we rotated the arrow of the radar accordingly, using theta.
As for the distance displayer, the following sections will teach us how to extend the directional radar to suit different situations that may happen in the design of the game, such as changing the projection plane.
Even after we have followed all the steps in this recipe, we are still not able to make it run, since this script works when it is integrated into a game. Hence, in order to test it, we need to construct a test scene. The one that is used in this example can be found in the resource bundle of this book. Otherwise, this can be done by placing a plane and then increasing the scale so that we can see it as the floor within the scene. Next, we can add a cube that represents our player and another cube that represents the target. Set the public variables by dragging both the cubes that you have created, and then click on play. If we move the two cubes in the scene in the Scene view, we can see the radar reacting in the Game view.
If we want to see the angles projected on the plane more clearly, just for debugging or learning purposes, we can use the Gizmos
function to better observe this process. We can do this by moving the plane down a little and then adding this function to our script:
void OnDrawGizmos() { Gizmos.color = Color.red; Gizmos.DrawLine(new Vector3 (player.position.x, 0, player.position.z), new Vector3 (target.position.x, 0, target.position.z)); Gizmos.color = Color.green; Gizmos.DrawRay (new Ray(new Vector3 (player.position.x, 0, player.position.z),(new Vector3 (player.forward.x, 0, player.forward.z).normalized))); }
It draws the two directions playerDir
and targetDir
in the Scene view.
Our game may have another floor that isn't the classical one that is found on the xz
plane. For example, if it is a 2D game, the entire game is on another plane, or if the gravity of the game changes at runtime, we should be able to change the projection plane accordingly.
If we only need to project onto the other two orthogonal planes, we just have to set the missing component to 0 (for example, in the xz
plane, the missing component is y
; in the xy
plane, it is z
). So, the two position projections along with the facing direction in the xy
plane are as follows:
Vector3 playerProjection = new Vector3 (player.position.x, player.position.y, 0); Vector3 targetProjection = new Vector3 (target.position.x, target.position.y, 0); Vector3 playerDir = (new Vector3 (player.forward.x, player.forward.y, 0).normalized);
And these are for the yz
plane:
Vector3 playerProjection = new Vector3 (0, player.position.y, player.position.z); Vector3 targetProjection = new Vector3 (0, target.position.y, target.position.z); Vector3 playerDir = (new Vector3 (0, player.forward.y, player.forward.z).normalized);
In general, as long as we have the normal of the plane where we want to project, we can use the following static function to project:
Vector3.ProjectOnPlane(vectorToProject, planeNormal)
Of course, in all of these cases, we also have to change the check inside the if
statement so that the arrow can rotate in the right direction.
Since the part of the script that detects the closest target in a set is very similar to the script used in the previous recipe, inside the There's More... section, we can just refer to it. This time, however, we don't have to store the distance but the target itself, so we use the following code:
float distance = float.MaxValue; Transform target; foreach(Transform t in targets){ if (distance > Vector3.Distance (player.position, t.position)){ distance = Vector3.Distance (player.position, t.position); target = t; } }
Again, the modification for adding a delay in the radar is very similar to one in the previous chapter, so just revisit that section to learn how to implement the coroutine.
The directional radar can be used in a number of different ways, such as detecting enemies in shooter games or assisting the player in locating treasure in platform games, especially if we integrate it with a Distance Displayer. Since this radar shows only the direction and not the distance, we can provide the player with an option to choose a Distance Displayer or a Directional Radar. Furthermore, we can incorporate both of them together in order to provide the player with more powerful equipment.
Lastly, we can consider implementing a 3D directional radar. In it, the arrow can rotate in all directions to point towards the target. In this case, we don't need to project the vectors, but we should be careful while calculating all the angles in order to rotate the arrow properly.
18.227.111.208