Adding a smooth tilt effect

There are many effects that can be applied to both 2D and 3D menus. Some of them can be small and subtle, such as a glow effect when the player moves the mouse cursor on a menu item. But while these effects are typically a nice touch for creating more dynamic interactions, they are usually complementary to 2D menus. 3D menus provide us with the ability to add another layer of movement along another dimension. As such, we can have the entire menu perform a range of different movements, such as rotation and tilting, both on its own and via user interaction. Since we are able to utilize the z axis, we are able to have elements projected in a different way. For instance, we are able to have the elements placed at various locations along the z axis. When we rotate items that are farther away, they rotate at a slower rate than those that are closer (to the camera). This is known as the parallax effect. This recipe will touch on some basic movements, such as moving and rotating the 3D UI element. These movements could be for the entire menu, by making it rotate according to the mouse's position.

How to do it...

  1. If we apply this effect to a menu that doesn't use the third dimension (all the elements have the z axis set to zero), it will just deform the menu and ultimately ruin the user experience. Therefore, it's important to use an adequate menu to apply this effect to - one that takes the z axis into consideration. Let's take the menu that we created in the previous recipe, or if you prefer, you can create another menu by keeping in mind to use the third dimension.
  2. Select the root of your menu. This is the element that has all the others as children. In the Inspector, go to Add Component | New Script and name it TiltEffectScript. Finally, click on Create and Add.

    Tip

    If your menu does not have a root, it is good practice to always have one. This is for keeping the contents of your menu in an ordered structure and applying modifications to all the elements in an easier way within your scripts. In order to create it on an existing menu, right-click on the Hierarchy Panel and then select Create Empty. Finally, rename it to MenuRoot. Use Rect Tool to modify the size of MenuRoot until it includes all the UI elements that belong to the menu. Now, select all the elements and drag them onto MenuRoot. We do this because it allows us to parent them to our root.

  3. Double-click on the script in order to edit it. Since we are not going to directly use the UI components, but just their transforms, in order to manipulate their rotation, we don't have to add the using UnityEngine.UI; statement at the beginning of the script.
  4. We need a public variable to set in the Inspector that stores the range of degrees to which the UI element can turn on the x axis and the y axis. Therefore, we can use Vector2 and set some arbitrary starting values:
    public Vector2 range = new Vector2(10f, 6f);
  5. Since we also want to give the possibility to decide the velocity of rotation, we need to create a public variable for it. Again, set its starting value arbitrarily:
    public float speed = 5f;
  6. Next, we need a vector variable to set at the beginning to zero. This variable will store the value by which the UI element has been rotated from the starting position in the previous frame. Again, since we need one value for each axis of rotation, we can use Vector2:
    private Vector2 tiltRotation = Vector2.zero;
  7. Since the rotation of the UI elements has to be calculated and updated for every frame, the implementation of the tilt effect will be in the Update() function. Since the mouse can move all over the screen, we have to, in some way, clamp its value between -1 and 1 so that we can also distinguish which side of the screen it is on. This is done in order to represent a percentage of how far the mouse is from the center of the screen. Let's start by calculating the two coordinate halves of the screen:
    float halfWidth = Screen.width / 2f;
    float halfHeight = Screen.height / 2f;
  8. After we have identified the position of the mouse in terms of how far it is from the center of the screen, we have to clamp its value along both the axes in order to get a kind of percentage of this distance from the center:
    float x = Mathf.Clamp((Input.mousePosition.x - halfWidth) / halfWidth, -1f, 1f);
    float y = Mathf.Clamp((Input.mousePosition.y - halfHeight) / halfHeight, -1f, 1f);
  9. At this point, we could calculate the value of the tilt rotation and assign it to our UI element. However, if we do this, it wouldn't be smooth. Therefore, we have to introduce a delay in the movement. By giving the x and y that we have calculated in the previous step, we have to start from the rotation that the UI element had in the last frame and make it rotate a little towards the rotation that it should have at the end. Therefore, to achieve this, we need to linearly interpolate. While doing this, we can pass the time from the last frame as the control parameter. In fact, if we assign this value to tiltRotation, we can start from this frame and move on to the next one. Furthermore, if we multiply deltaTime with our speed stored in speed, we can control how smooth the rotation will be:
    tiltRotation = Vector2.Lerp(tiltRotation, new Vector2(x, y), Time.deltaTime * speed);
  10. Finally, we have to assign the new rotation to the UI element, so by converting the Euler angles in Quaternion, we can make the assignment:
    transform.localRotation = originalRotation * Quaternion.Euler(-tiltRotation.y * range.y, tiltRotation.x * range.x, 0f);
  11. Save the script and your work is done. The result at runtime is better when it is moving because it provides more dynamic visuals, as opposed to a static image. However, as shown in the following screenshot, we are still able to gain some information about what we will see in the final outcome:
    How to do it...

There's more...

It is possible to slightly change the script to make it more customizable by designers. This is the aim of the following sections that will teach us how to do this.

Starting from the original rotation

Some menus can have an initial rotation that determines where they start with the tilt effect. In order to do this, we need to store the initial rotation in a private variable, like this:

private Quaternion originalRotation;

Then, we have to initialize it in the Start() function with this line:

originalRotation = transform.localRotation;

Finally, in the last line of our Update() function, we just multiply the new rotation with the original one:

transform.localRotation = originalRotation * Quaternion.Euler(-tiltRotation.y * range.y, tiltRotation.x * range.x, 0f);

Now, every time the tilt effect is applied, it will start from the initial rotation of the UI element that we have set.

Converting the speed in the smoothness factor

In some instances, we might want to provide an easier way for designers to tweak the smoothness of the rotation instead of the velocity. In this case, we can replace the speed variable with this one:

public float smoothnessFactor = 0.2f;

Then, we use smoothnessFactor in the code in this way:

tiltRotation = Vector2.Lerp(tiltRotation, new Vector2(x, y), Time.deltaTime * (1f/ smoothnessFactor);

Inverting the axis

In some games, both the axes are inverted, and in others, only one axis is. Since we have scripted our tilt effect to include a range vector that can have negative values, we can achieve inversion by changing the sign to the components of the range vector. Of course, it is possible to have only one negative value to invert a specific axis. Furthermore, to simplify designers' lives, we can keep the values for the vector positive. Thus, we just need to change the signs of the vector components when they are utilized in the script. This means negative to positive and vice versa. In particular, we have to change the following line in this way:

transform.localRotation = originalRotation * Quaternion.Euler(-tiltRotation.y * -range.y, tiltRotation.x * -range.x, 0f);

Asymmetric range for the rotation

If we want to rotate our UI element using an asymmetric tilt effect, it could be a bit tricky, because it is a little more complicated than other concepts that we have previously covered. An asymmetric tilt effect means that when the mouse goes from one side of the screen to another, the range of rotation changes. Therefore, we need another range vector for the symmetric part:

public Vector2 symmetricRange = new Vector2(7f, 4f);

Now, when the script is running, we have to use one vector or the other depending on where the mouse is. Hence, we have to use an if statement by checking this and applying one range vector or the other when we rotate the UI element:

if(tiltRotation.y > 0 && tiltRotation.x > 0)
  transform.localRotation = originalRotation * Quaternion.Euler(-tiltRotation.y * range.y, tiltRotation.x * range.x, 0f);
if(tiltRotation.y < 0 && tiltRotation.x > 0)
  transform.localRotation = originalRotation * Quaternion.Euler(-tiltRotation.y * symmetricRange.y, tiltRotation.x * range.x, 0f);
if(tiltRotation.y > 0 && tiltRotation.x < 0)
  transform.localRotation = originalRotation * Quaternion.Euler(-tiltRotation.y * range.y, tiltRotation.x * symmetricRange.x, 0f);
if(tiltRotation.y < 0 && tiltRotation.x < 0)
  transform.localRotation = originalRotation * Quaternion.Euler(-tiltRotation.y * symmetricRange.y, tiltRotation.x * symmetricRange.x, 0f);

Changing the reference of the mouse from the screen to an arbitrary rect

In some cases, we don't want the area in which the tilt effect takes place to be extended along all of the screen size. In such cases, we should replace it with an arbitrary Rect. In this case, when we calculate the x and y values, we have to use the size of Rect, as follows:

float x = Mathf.Clamp((Input.mousePosition.x - halfRectWidth) / halfRectWidth, -1f, 1f);
float y = Mathf.Clamp((Input.mousePosition.y - halfRectHeight) / halfRectHeight, -1f, 1f);

If Rect is not centered in the middle of the screen, remember to add the offset of the position of Rect.

Tip

We can also notice that if some part of the Rect is outside the screen, the clamp will never be 1 or -1. Therefore, the rotation will not be complete. While we may want to do this, it is better practice to tweak the range vector in order to achieve the same effect.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.186.79