Sagui Itay - Unity Assets, software development and mobile games
Unity Tutorial - Holographic card - Featured

Unity Tutorial: Multiple Parallax/Holographic cards

In the previous Unity Tutorial, we saw how to create a Parallax/Holographic card. In this post, we’ll improve the performance of the ParallaxPanelScript by supporting multiple cards, without adding too much overhead.

Let’s extract the logic of a card

We’ll extract the basic logic of the holographic effect to a dedicated, non-MonoBehavior class.

[Serializable]
public class Card
{
    public RectTransform hologram;
    public RectTransform rect;

    private float m_centerX;
    private float m_centerY;
    private Vector3 m_targetEulerAngles = Vector3.zero;

    public void Init(float depth)
    {
        m_centerX = rect.sizeDelta.x / 2f;
        m_centerY = rect.sizeDelta.y / 2f;

        hologram.position = new Vector3(hologram.position.x, hologram.position.y, -depth);
    }

    public void FixedUpdate(Vector2 maxRotation, float speed)
    {
        //Difference between the mouse pos and the panel's position
        Vector2 diff = (Vector2)rect.position - (Vector2)Input.mousePosition;

        //If the mouse is inside the rect/panel [...]
        if (Mathf.Abs(diff.x) <= m_centerX && Mathf.Abs(diff.y) <= m_centerY)
        {
            m_targetEulerAngles = new Vector3(
                //Rotates along the X axis, based on the Y distance from the center
                maxRotation.x * -Mathf.Clamp(diff.y / m_centerY, -1, 1),
                //Rotates along the Y axis, based on the X distance from the center
                maxRotation.y * Mathf.Clamp(diff.x / m_centerX, -1, 1),
                //No rotation along the Z axis
                0);
        }
        else  //Mouse is outside the rect, target euler is zero
        {
            m_targetEulerAngles = Vector3.zero;
        }

        //Lerps the rotation
        var distanceToTarget = (rect.eulerAngles - m_targetEulerAngles).magnitude;
        if (distanceToTarget > 0.01)
        {
            rect.eulerAngles = AngleLerp(rect.eulerAngles, m_targetEulerAngles, speed * Time.deltaTime);
        }
        else
        {
            rect.eulerAngles = m_targetEulerAngles;
        }
    }

    private static Vector3 AngleLerp(Vector3 startAngle, Vector3 finishAngle, float deltaTime)
    {
        // Lerp each axis separately using the LerpAngle method
        return new Vector3(
            Mathf.LerpAngle(startAngle.x, finishAngle.x, deltaTime),
            Mathf.LerpAngle(startAngle.y, finishAngle.y, deltaTime),
            startAngle.z // No rotation along the Z axis - Mathf.LerpAngle(startAngle.z, finishAngle.z, deltatime)
            );
    }
}

This will allow us to execute the logic for multiple cards with a single FixedUpdate invocation. Relying on Unity’s messages makes things easy, but if we have lots of scripts, this takes its toll.

So, a single MonoBehavior it is

We’ll create a single MonoBehavior script – MultiParallaxPanelsScript – that will serve as the host for all of our cards. It will receive the FixedUpdate message, and pass it along to the various cards. In this tutorial, I’ve selected to have the various settings configurable at the “global” level, so fields such as depth, maxRotation and speed are defined within MultiParallaxPanelsScript. An alternative would be to set them for each card separately. Or perhaps define a global default value, but allow specific cards to override the defaults:

public class MultiParallaxPanelsScript : MonoBehaviour
{
    public Card[] cards;

    [Range(0, 20)]
    public float depth = 10;

    [Header("Rotation")]

    // Set to 0 if you want don't want it to rotate along this axis
    [Vector2Range(0, 50, 0, 50)]
    public Vector2 maxRotation;

    // Speed for the rotation
    [Range(0, 10)]
    public float speed;

    private void Start()
    {
        foreach (var card in cards)
            card.Init(depth);
    }

    private void FixedUpdate()
    {
        foreach (var card in cards)
            card.FixedUpdate(maxRotation, speed);
    }
}

The results are expected to look very similar to those in our original tutorial, only slightly faster and putting less pressure on the CPU:

Unity Tutorial Multiple Parallax-Holographic cards