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: