using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace HighlightingSystem
{
public enum LoopMode
{
Once,
Loop,
PingPong,
ClampForever,
}
public enum Easing
{
Linear,
QuadIn,
QuadOut,
QuadInOut,
CubicIn,
CubicOut,
CubicInOut,
SineIn,
SineOut,
SineInOut,
}
public enum RendererFilterMode
{
None,
Include,
Exclude,
}
[AddComponentMenu("Highlighting System/Highlighter", 0)]
public class Highlighter : HighlighterCore
{
#region Constants
protected const float HALFPI = Mathf.PI * 0.5f;
#endregion
#region Inspector Fields
/* General */
///
/// When set to true - highlighting will be rendered on top of all other geometry.
///
public bool overlay
{
get { return _overlay; }
set { _overlay = value; }
}
///
/// Controls occluder mode.
///
public bool occluder
{
get { return _occluder; }
set { _occluder = value; }
}
/* Tween */
public bool tween
{
get { return _tween; }
set { TweenSet(value); }
}
public Gradient tweenGradient
{
get { return _tweenGradient; }
set { _tweenGradient = value; }
}
public float tweenDuration
{
get { return _tweenDuration; }
set { _tweenDuration = value; ValidateRanges(); }
}
public float tweenDelay
{
get { return _tweenDelay; }
set { _tweenDelay = value; }
}
public bool tweenUseUnscaledTime
{
get { return _tweenUseUnscaledTime; }
set
{
if (_tweenUseUnscaledTime != value)
{
float delta = GetTweenTime() - _tweenStart;
_tweenUseUnscaledTime = value;
_tweenStart = GetTweenTime() - delta;
}
}
}
public LoopMode tweenLoop
{
get { return _tweenLoop; }
set { _tweenLoop = value; }
}
public Easing tweenEasing
{
get { return _tweenEasing; }
set { _tweenEasing = value; }
}
public bool tweenReverse
{
get { return _tweenReverse; }
set { _tweenReverse = value; }
}
public int tweenRepeatCount
{
get { return _tweenRepeatCount; }
set { _tweenRepeatCount = value; }
}
/* Constant */
public bool constant
{
get { return _constant; }
set { ConstantSet(value); }
}
public Color constantColor
{
get { return _constantColor; }
set { _constantColor = value; }
}
public float constantFadeTime
{
set
{
_constantFadeInTime = value;
_constantFadeOutTime = value;
ValidateRanges();
ConstantSet();
}
}
public float constantFadeInTime
{
get { return _constantFadeInTime; }
set
{
_constantFadeInTime = value;
ValidateRanges();
if (_constant) { ConstantSet(); }
}
}
public float constantFadeOutTime
{
get { return _constantFadeOutTime; }
set
{
_constantFadeOutTime = value;
ValidateRanges();
if (!_constant) { ConstantSet(); }
}
}
public bool constantUseUnscaledTime
{
get { return _constantUseUnscaledTime; }
set
{
if (_constantUseUnscaledTime != value)
{
float delta = GetConstantTime() - _constantStart;
_constantUseUnscaledTime = value;
_constantStart = GetConstantTime() - delta;
}
}
}
public Easing constantEasing
{
get { return _constantEasing; }
set { _constantEasing = value; }
}
public RendererFilterMode filterMode
{
get { return _filterMode; }
set
{
if (_filterMode != value)
{
_filterMode = value;
// Update highlighted Renderers
SetDirty();
}
}
}
///
/// Make sure to trigger SetDirty() after modifying this list
///
public List filterList
{
get { return _filterList; }
}
protected override RendererFilter rendererFilterToUse
{
get
{
if (rendererFilter != null)
{
return rendererFilter;
}
else if (_filterMode == RendererFilterMode.None)
{
return globalRendererFilter != null ? globalRendererFilter : DefaultRendererFilter;
}
else if (_filterMode == RendererFilterMode.Include)
{
return TransformFilterInclude;
}
else if (_filterMode == RendererFilterMode.Exclude)
{
return TransformFilterExclude;
}
// Should never happen
return DefaultRendererFilter;
}
}
#endregion
#region Protected Fields
// General
[SerializeField] protected bool _overlay;
[SerializeField] protected bool _occluder;
// Hover (do not serialize this!)
protected Color _hoverColor = Color.red;
protected int _hoverFrame = -1;
// Tween
[SerializeField] protected bool _tween = false;
[SerializeField] protected Gradient _tweenGradient = new Gradient()
{
colorKeys = new GradientColorKey[]
{
new GradientColorKey(new Color(0f, 1f, 1f, 1f), 0f),
new GradientColorKey(new Color(0f, 1f, 1f, 1f), 1f),
},
alphaKeys = new GradientAlphaKey[]
{
new GradientAlphaKey(0f, 0f),
new GradientAlphaKey(1f, 1f),
}
};
[SerializeField] protected float _tweenDuration = 1f;
[SerializeField] protected bool _tweenReverse = false;
[SerializeField] protected LoopMode _tweenLoop = LoopMode.PingPong;
[SerializeField] protected Easing _tweenEasing = Easing.Linear;
[SerializeField] protected float _tweenDelay = 0f;
[SerializeField] protected int _tweenRepeatCount = -1;
[SerializeField] protected bool _tweenUseUnscaledTime = false;
// Constant highlighting mode (with optional fade in/out transition)
[SerializeField] protected bool _constant = false;
[SerializeField] protected Color _constantColor = Color.yellow;
[SerializeField] protected float _constantFadeInTime = 0.1f;
[SerializeField] protected float _constantFadeOutTime = 0.25f;
[SerializeField] protected Easing _constantEasing = Easing.Linear;
[SerializeField] protected bool _constantUseUnscaledTime = false;
[SerializeField] protected RendererFilterMode _filterMode = RendererFilterMode.None;
[SerializeField] protected List _filterList = new List();
// Runtime only (do not serialize this!)
protected bool _tweenEnabled;
protected float _tweenStart; // Time when the tween has started
protected bool _constantEnabled;
protected float _constantStart;
protected float _constantDuration;
#endregion
#region Protected Accessors
protected bool hover
{
get { return _hoverFrame == Time.frameCount; }
set { _hoverFrame = value ? Time.frameCount : -1; }
}
// constantValue is always increasing from 0 to 1 (for both fade in and out transitions)
protected float constantValue
{
get { return _constantDuration > 0f ? Mathf.Clamp01((GetConstantTime() - _constantStart) / _constantDuration) : 1f; }
}
#endregion
#region MonoBehaviour
//
protected virtual void OnDidApplyAnimationProperties()
{
ValidateAll();
}
#if UNITY_EDITOR
//
void OnValidate()
{
ValidateAll();
}
//
void Reset()
{
ValidateAll();
}
#endif
#endregion
#region MonoBehaviour Overrides
//
protected override void OnEnableSafe()
{
ValidateAll();
}
//
protected override void OnDisableSafe()
{
// Make sure transition won't continue if component will be re-enabled
_tweenEnabled = false;
_constantEnabled = false;
_constantStart = GetConstantTime() - _constantDuration;
}
#endregion
#region Validation
//
protected void ValidateAll()
{
ValidateRanges();
TweenSet();
ConstantSet();
SetDirty();
}
//
protected void ValidateRanges()
{
if (_tweenDuration < 0f) { _tweenDuration = 0f; }
if (_constantFadeInTime < 0f) { _constantFadeInTime = 0f; }
if (_constantFadeOutTime < 0f) { _constantFadeInTime = 0f; }
}
#endregion
#region Public Methods
///
/// Turn on one-frame highlighting with specified color.
/// Can be called multiple times per update, color only from the latest call will be used.
///
///
/// Highlighting color.
///
public void Hover(Color color)
{
_hoverColor = color;
hover = true;
}
///
/// Fade in constant highlighting using specified transition duration.
///
///
/// Transition time.
///
public void ConstantOn(float fadeTime = 0.25f)
{
ConstantSet(fadeTime, true);
}
///
/// Fade in constant highlighting using specified color and transition duration.
///
///
/// Constant highlighting color.
///
///
/// Transition duration.
///
public void ConstantOn(Color color, float fadeTime = 0.25f)
{
_constantColor = color;
ConstantSet(fadeTime, true);
}
///
/// Fade out constant highlighting using specified transition duration.
///
///
/// Transition time.
///
public void ConstantOff(float fadeTime = 0.25f)
{
ConstantSet(fadeTime, false);
}
///
/// Switch constant highlighting using specified transition duration.
///
///
/// Transition time.
///
public void ConstantSwitch(float fadeTime = 0.25f)
{
ConstantSet(fadeTime, !_constant);
}
///
/// Turn on constant highlighting immediately (without fading in).
///
public void ConstantOnImmediate()
{
ConstantSet(0f, true);
}
///
/// Turn on constant highlighting using specified color immediately (without fading in).
///
///
/// Constant highlighting color.
///
public void ConstantOnImmediate(Color color)
{
_constantColor = color;
ConstantSet(0f, true);
}
///
/// Turn off constant highlighting immediately (without fading out).
///
public void ConstantOffImmediate()
{
ConstantSet(0f, false);
}
///
/// Switch constant highlighting immediately (without fading in/out).
///
public void ConstantSwitchImmediate()
{
ConstantSet(0f, !_constant);
}
///
/// Turn off all types of highlighting (occlusion mode remains intact).
///
public void Off()
{
hover = false;
TweenSet(false);
ConstantSet(0f, false);
}
//
public void TweenStart()
{
TweenSet(true);
}
//
public void TweenStop()
{
TweenSet(false);
}
// Updates tween state. (!) Sets _tween value.
public void TweenSet(bool value)
{
_tween = value;
if (_tweenEnabled != _tween)
{
_tweenEnabled = _tween;
_tweenStart = GetTweenTime();
}
}
// Updates constant state. (!) Sets _constant value.
public void ConstantSet(float fadeTime, bool value)
{
// Order matters. Should always set _constantDuration prior to _constantEnabled
// Transition duration
if (fadeTime < 0f) { fadeTime = 0f; }
if (_constantDuration != fadeTime)
{
float timeNow = GetConstantTime();
// Recalculate start time if duration changed
_constantStart = _constantDuration > 0f ? timeNow - (fadeTime / _constantDuration) * (timeNow - _constantStart) : timeNow - fadeTime;
_constantDuration = fadeTime;
}
// Transition target
_constant = value;
if (_constantEnabled != _constant)
{
_constantEnabled = _constant;
// Recalculate start time if value changed
_constantStart = GetConstantTime() - _constantDuration * (1f - constantValue);
}
}
#endregion
#region Protected Methods
// Helper
protected void TweenSet()
{
TweenSet(_tween);
}
// Helper
protected void ConstantSet()
{
ConstantSet(_constant);
}
// Helper
protected void ConstantSet(bool value)
{
ConstantSet(value ? constantFadeInTime : _constantFadeOutTime, value);
}
// Updates highlighting color
protected override void UpdateHighlighting()
{
// Hover
if (hover)
{
color = _hoverColor;
mode = _overlay ? HighlighterMode.Overlay : HighlighterMode.Default;
return;
}
// Tween
if (_tweenEnabled)
{
float delta = GetTweenTime() - (_tweenStart + _tweenDelay);
if (delta >= 0f)
{
float t = _tweenDuration > 0f ? delta / _tweenDuration : 0f;
t = Loop(t, _tweenLoop, _tweenReverse, _tweenRepeatCount);
if (t >= 0f)
{
t = Ease(t, _tweenEasing);
color = _tweenGradient.Evaluate(t);
mode = _overlay ? HighlighterMode.Overlay : HighlighterMode.Default;
return;
}
}
}
// Constant
float c = _constantEnabled ? constantValue : 1f - constantValue;
if (c > 0f)
{
c = Ease(c, _constantEasing);
color = _constantColor;
color.a *= c;
mode = _overlay ? HighlighterMode.Overlay : HighlighterMode.Default;
return;
}
// Occluder
if (_occluder)
{
mode = HighlighterMode.Occluder;
return;
}
// Disabled
mode = HighlighterMode.Disabled;
}
#endregion
#region Protected Methods
//
protected bool TransformFilterInclude(Renderer renderer, List submeshIndices)
{
Transform child = renderer.transform;
for (int i = 0; i < _filterList.Count; i++)
{
Transform parent = _filterList[i];
if (parent == null) { continue; }
// Highlight if child of inclusion list transform
if (child.IsChildOf(parent))
{
// Highlight all submeshes
submeshIndices.Add(-1);
return true;
}
}
return false;
}
//
protected bool TransformFilterExclude(Renderer renderer, List submeshIndices)
{
Transform child = renderer.transform;
for (int i = 0; i < _filterList.Count; i++)
{
Transform parent = _filterList[i];
if (parent == null) { continue; }
// Do not highlight if child of exclusion list transform
if (child.IsChildOf(parent)) { return false; }
}
// Highlight all submeshes
submeshIndices.Add(-1);
return true;
}
//
protected float Loop(float x, LoopMode loop, bool reverse = false, int repeatCount = -1)
{
float y = -1f;
switch (loop)
{
default:
case LoopMode.Once:
if (x >= 0f && x <= 1f) { y = x; }
break;
case LoopMode.Loop:
int n1 = Mathf.FloorToInt(x);
if (repeatCount < 0 || n1 < repeatCount)
{
y = x - n1;
}
break;
case LoopMode.PingPong:
// Performs 0-1-0 transition in tweenDuration time
//int n2 = Mathf.FloorToInt(x);
//if (repeatCount < 0 || n2 < repeatCount)
//{
// y = 1f - Mathf.Abs((x - n2) * 2f - 1f);
//}
// Performs 0-1-0 transition in 2 * tweenDuration time,
// so the 0-1 transition is performed with the same speed as in Loop mode.
int n2 = Mathf.FloorToInt(x * 0.5f);
if (repeatCount < 0 || n2 < repeatCount)
{
y = 1f - Mathf.Abs(x - n2 * 2f - 1f);
}
break;
case LoopMode.ClampForever:
y = Mathf.Clamp01(x);
break;
}
// Reverse. Check y for positive value since there is no loop if it's negative.
if (y >= 0f && reverse) { y = 1f - y; }
return y;
}
//
protected float Ease(float x, Easing easing)
{
x = Mathf.Clamp01(x);
float y;
switch (easing)
{
// Linear
default:
case Easing.Linear:
y = x;
break;
// Quad
case Easing.QuadIn:
y = x * x;
break;
case Easing.QuadOut:
y = -x * (x - 2f);
break;
case Easing.QuadInOut:
y = x < 0.5f ? 2f * x * x : 2f * x * (2f - x) - 1f;
break;
// Cubic
case Easing.CubicIn:
y = x * x * x;
break;
case Easing.CubicOut:
x = x - 1f;
y = x * x * x + 1f;
break;
case Easing.CubicInOut:
if (x < 0.5f)
{
y = 4f * x * x * x;
}
else
{
x = 2f * x - 2f;
y = 0.5f * (x * x * x + 2f);
}
break;
// Sine
case Easing.SineIn:
y = 1f - Mathf.Cos(x * HALFPI);
break;
case Easing.SineOut:
y = Mathf.Sin(x * HALFPI);
break;
case Easing.SineInOut:
y = -0.5f * (Mathf.Cos(x * Mathf.PI) - 1f);
break;
//y = x * x * (3f - 2f * x);
}
return y;
}
//
protected float GetTweenTime()
{
return _tweenUseUnscaledTime ? Time.unscaledTime : Time.time;
}
//
protected float GetConstantTime()
{
return _constantUseUnscaledTime ? Time.unscaledTime : Time.time;
}
#endregion
#region Static Methods
// Color helper
static public Color HSVToRGB(float hue, float saturation, float value)
{
float x = 6f * Mathf.Clamp01(hue);
saturation = Mathf.Clamp01(saturation);
value = Mathf.Clamp01(value);
return new Color
(
value * (1f + (Mathf.Clamp01(Mathf.Max(2f - x, x - 4f)) - 1f) * saturation),
value * (1f + (Mathf.Clamp01(Mathf.Min(x, 4f - x)) - 1f) * saturation),
value * (1f + (Mathf.Clamp01(Mathf.Min(x - 2f, 6f - x)) - 1f) * saturation)
);
}
#endregion
#region DEPRECATED
private GradientColorKey[] flashingColorKeys = new GradientColorKey[]
{
new GradientColorKey(new Color(0f, 1f, 1f, 0f), 0f),
new GradientColorKey(new Color(0f, 1f, 1f, 1f), 1f)
};
private GradientAlphaKey[] flashingAlphaKeys = new GradientAlphaKey[]
{
new GradientAlphaKey(1f, 0f),
new GradientAlphaKey(1f, 1f),
};
///
/// DEPRECATED. Use hover = true; instead.
///
[System.Obsolete]
public void On()
{
hover = true;
}
///
/// DEPRECATED. Use Hover(Color color) instead.
///
[System.Obsolete]
public void On(Color color)
{
Hover(color);
}
///
/// DEPRECATED. Use Hover(new Color(1f, 0f, 0f, 1f)); instead.
///
[System.Obsolete]
public void OnParams(Color color)
{
_hoverColor = color;
}
///
/// DEPRECATED. Use constantColor = new Color(0f, 1f, 1f, 1f); instead.
///
[System.Obsolete]
public void ConstantParams(Color color)
{
_constantColor = color;
}
///
/// DEPRECATED. Use tweenGradient and tweenDuration instead.
///
[System.Obsolete]
public void FlashingParams(Color color1, Color color2, float freq)
{
flashingColorKeys[0].color = color1;
flashingColorKeys[1].color = color2;
_tweenGradient.SetKeys(flashingColorKeys, flashingAlphaKeys);
_tweenDuration = 1f / freq;
}
///
/// DEPRECATED. Use TweenStart() instead.
///
[System.Obsolete]
public void FlashingOn()
{
TweenSet(true);
}
///
/// DEPRECATED. Use tweenGradient instead.
///
[System.Obsolete]
public void FlashingOn(Color color1, Color color2)
{
flashingColorKeys[0].color = color1;
flashingColorKeys[1].color = color2;
_tweenGradient.SetKeys(flashingColorKeys, flashingAlphaKeys);
TweenSet(true);
}
///
/// DEPRECATED. Use tweenGradient and tweenDuration instead.
///
[System.Obsolete]
public void FlashingOn(Color color1, Color color2, float freq)
{
flashingColorKeys[0].color = color1;
flashingColorKeys[1].color = color2;
_tweenGradient.SetKeys(flashingColorKeys, flashingAlphaKeys);
_tweenDuration = 1f / freq;
TweenSet(true);
}
///
/// DEPRECATED. Use tweenDuration instead.
///
[System.Obsolete]
public void FlashingOn(float freq)
{
_tweenDuration = 1f / freq;
TweenSet(true);
}
///
/// DEPRECATED. Use TweenStop() instead.
///
[System.Obsolete]
public void FlashingOff()
{
TweenSet(false);
}
///
/// DEPRECATED. Use TweenStart() and TweenStop() instead.
///
[System.Obsolete]
public void FlashingSwitch()
{
tween = !tween;
}
#endregion
}
}