using UnityEngine;
using System;
namespace Unity.Cinemachine
{
/// This structure holds settings for screen-space composition.
[Serializable]
public struct ScreenComposerSettings
{
/// Screen position for target. The camera will adjust to position the
/// tracked object here. 0 is screen center, and +0.5 or -0.5 is screen edge
[Tooltip("Screen position for target. The camera will adjust to position the "
+ "tracked object here. 0 is screen center, and +0.5 or -0.5 is screen edge")]
//[DelayedVector] // interferes with game guide refresh
public Vector2 ScreenPosition;
/// Settings for DeadZone, which is an area within which the camera will not adjust itself.
[Serializable]
public struct DeadZoneSettings
{
/// Enables the Dead Zone settings
public bool Enabled;
/// The camera will not adjust if the target is within this range of the screen position.
/// Full screen size is 1.
[Tooltip("The camera will not adjust if the target is within this range of the "
+ "screen position. Full screen size is 1.")]
//[DelayedVector] // interferes with game guide refresh
public Vector2 Size;
}
/// The camera will not adjust if the target is within this range of the screen position
[Tooltip("The camera will not adjust if the target is within this range of the screen position")]
[FoldoutWithEnabledButton]
public DeadZoneSettings DeadZone;
/// The target will not be allowed to be outside this region.
/// When the target is within this region, the camera will gradually adjust to re-align
/// towards the desired position, depending on the damping speed
[Serializable]
public struct HardLimitSettings
{
/// Enables the Hard Limit settings
public bool Enabled;
/// The target will not be allowed to be outside this region.
/// When the target is within this region, the camera will gradually adjust to re-align
/// towards the desired position, depending on the damping speed.
/// Full screen size is 1
[Tooltip("The target will not be allowed to be outside this region. "
+ "When the target is within this region, the camera will gradually adjust to re-align "
+ "towards the desired position, depending on the damping speed. "
+ "Full screen size is 1")]
//[DelayedVector] // interferes with game guide refresh
public Vector2 Size;
/// A zero Offset means that the hard limits will be centered around the target screen position.
/// A nonzero Offset will uncenter the hard limits relative to the target screen position.
///
[Tooltip("A zero Offset means that the hard limits will be centered around the target screen position. "
+ "A nonzero Offset will uncenter the hard limits relative to the target screen position.")]
//[DelayedVector] // interferes with game guide refresh
public Vector2 Offset;
}
/// The target will not be allowed to be outside this region.
/// When the target is within this region, the camera will gradually adjust to re-align
/// towards the desired position, depending on the damping speed
[Tooltip("The target will not be allowed to be outside this region. "
+ "When the target is within this region, the camera will gradually adjust to re-align "
+ "towards the desired position, depending on the damping speed")]
[FoldoutWithEnabledButton]
public HardLimitSettings HardLimits;
/// Clamps values to the expected ranges
public void Validate()
{
ScreenPosition.x = Mathf.Clamp(ScreenPosition.x, -1.5f, 1.5f);
ScreenPosition.y = Mathf.Clamp(ScreenPosition.y, -1.5f, 1.5f);
DeadZone.Size.x = Mathf.Clamp(DeadZone.Size.x, 0f, 2f);
DeadZone.Size.y = Mathf.Clamp(DeadZone.Size.y, 0f, 2f);
HardLimits.Size = new Vector2(
Mathf.Clamp(HardLimits.Size.x, DeadZone.Size.x, 3),
Mathf.Clamp(HardLimits.Size.y, DeadZone.Size.y, 3));
HardLimits.Offset.x = Mathf.Clamp(HardLimits.Offset.x, -1f, 1f);
HardLimits.Offset.y = Mathf.Clamp(HardLimits.Offset.y, -1f, 1f);
}
/// Get the effective dead zone size, taking enabled state into account
public Vector2 EffectiveDeadZoneSize => DeadZone.Enabled ? DeadZone.Size : Vector2.zero;
/// Get the effective hard limits size, taking enabled state into account
public Vector2 EffectiveHardLimitSize => HardLimits.Enabled ? HardLimits.Size : new Vector2(3, 3);
/// Get/set the screenspace rect for the dead zone region. This also defines screen position
public Rect DeadZoneRect
{
get
{
var deadZoneSize = EffectiveDeadZoneSize;
return new Rect(ScreenPosition - deadZoneSize * 0.5f + new Vector2(0.5f, 0.5f), deadZoneSize);
}
set
{
var deadZoneSize = EffectiveDeadZoneSize;
if (DeadZone.Enabled)
{
deadZoneSize = new Vector2(Mathf.Clamp(value.width, 0, 2), Mathf.Clamp(value.height, 0, 2));
DeadZone.Size = deadZoneSize;
}
ScreenPosition = new Vector2(
Mathf.Clamp(value.x - 0.5f + deadZoneSize.x * 0.5f, -1.5f, 1.5f),
Mathf.Clamp(value.y - 0.5f + deadZoneSize.y * 0.5f, -1.5f, 1.5f));
HardLimits.Size = new Vector2(
Mathf.Clamp(HardLimits.Size.x, deadZoneSize.x, 3),
Mathf.Clamp(HardLimits.Size.y, deadZoneSize.y, 3));
}
}
/// Get/set the screenspace rect for the hard limits.
public Rect HardLimitsRect
{
get
{
if (!HardLimits.Enabled)
return new Rect(-EffectiveHardLimitSize * 0.5f, EffectiveHardLimitSize);
var r = new Rect(ScreenPosition - HardLimits.Size * 0.5f + new Vector2(0.5f, 0.5f), HardLimits.Size);
var deadZoneSize = EffectiveDeadZoneSize;
r.position += new Vector2(
HardLimits.Offset.x * 0.5f * (HardLimits.Size.x - deadZoneSize.x),
HardLimits.Offset.y * 0.5f * (HardLimits.Size.y - deadZoneSize.y));
return r;
}
set
{
HardLimits.Size.x = Mathf.Clamp(value.width, 0, 6f);
HardLimits.Size.y = Mathf.Clamp(value.height, 0, 6f);
DeadZone.Size.x = Mathf.Min(DeadZone.Size.x, HardLimits.Size.x);
DeadZone.Size.y = Mathf.Min(DeadZone.Size.y, HardLimits.Size.y);
// GML todo: set bias
}
}
///
/// Linear interpolation between 2 settings objects.
///
/// First settings object
/// Second settings object
/// Interpolation amount: 0 is a, 1 is b
///
public static ScreenComposerSettings Lerp(in ScreenComposerSettings a, in ScreenComposerSettings b, float t)
{
return new ScreenComposerSettings
{
ScreenPosition = Vector2.Lerp(a.ScreenPosition, b.ScreenPosition, t),
DeadZone = new ()
{
Enabled = a.DeadZone.Enabled || b.DeadZone.Enabled,
Size = Vector2.Lerp(a.EffectiveDeadZoneSize, b.EffectiveDeadZoneSize, t)
},
HardLimits = new ()
{
Enabled = a.HardLimits.Enabled || b.HardLimits.Enabled,
Size = Vector2.Lerp(a.EffectiveHardLimitSize, b.EffectiveHardLimitSize, t),
Offset = Vector2.Lerp(a.HardLimits.Offset, b.HardLimits.Offset, t)
}
};
}
///
/// Tests whether 2 ScreenComposerSettings are approximately equal.
///
/// First settings object
/// Second settings object
/// True if all fields are equal to within a small epsilon value
public static bool Approximately(in ScreenComposerSettings a, in ScreenComposerSettings b)
{
return Mathf.Approximately(a.ScreenPosition.x, b.ScreenPosition.x)
&& Mathf.Approximately(a.ScreenPosition.y, b.ScreenPosition.y)
&& Mathf.Approximately(a.EffectiveDeadZoneSize.x, b.EffectiveDeadZoneSize.x)
&& Mathf.Approximately(a.EffectiveDeadZoneSize.y, b.EffectiveDeadZoneSize.y)
&& Mathf.Approximately(a.EffectiveHardLimitSize.x, b.EffectiveHardLimitSize.x)
&& Mathf.Approximately(a.EffectiveHardLimitSize.y, b.EffectiveHardLimitSize.y)
&& Mathf.Approximately(a.HardLimits.Offset.x, b.HardLimits.Offset.x)
&& Mathf.Approximately(a.HardLimits.Offset.y, b.HardLimits.Offset.y);
}
/// The default screen composition
public static ScreenComposerSettings Default => new ()
{
DeadZone = new () { Enabled = false, Size = new Vector2(0.2f, 0.2f) },
HardLimits = new () { Enabled = false, Size = new Vector2(0.8f, 0.8f) }
};
}
}