Insanely huge initial commit

This commit is contained in:
2026-02-21 17:04:05 -08:00
parent 9cdd36191a
commit 613d75914a
22525 changed files with 4035207 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 00efa41e48d5e5e4a8761a47913301d7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 40ee8b5e74c70d84aa6c610f4ad0dc3e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,93 @@
using UnityEngine;
using System.Collections.Generic;
namespace Febucci.UI.Core
{
/// <summary>
/// Represents visible characters in the text (including sprites and excluding bars)
/// </summary>
struct Character
{
#pragma warning disable 0649
internal bool initialized;
#pragma warning restore 0649
public float disappearancesMaxDuration;
public bool isDisappearing;
public bool wantsToDisappear;
public float appearancesMaxDuration;
public int[] indexBehaviorEffects;
public int[] indexAppearanceEffects;
public int[] indexDisappearanceEffects;
public CharacterSourceData sources;
public CharacterData data;
public void ResetVertices()
{
for (byte i = 0; i < sources.vertices.Length; i++)
{
data.vertices[i] = sources.vertices[i];
}
}
public void ResetColors()
{
for (byte i = 0; i < sources.colors.Length; i++)
{
data.colors[i] = sources.colors[i];
}
}
public void Hide()
{
for (byte i = 0; i < sources.vertices.Length; i++)
{
data.vertices[i] = Vector3.zero;
}
}
}
//Original character data
struct CharacterSourceData
{
public Color32[] colors;
public Vector3[] vertices;
}
/// <summary>
/// Contains characters data that can be modified during TextAnimator effects.
/// </summary>
public struct CharacterData
{
/// <summary>
/// Time passed since the character is visible (or just started the appearance effect).
/// </summary>
public float passedTime;
/// <summary>
/// A character's vertices colors.
/// </summary>
/// <remarks>
/// The array size is usually <see cref="TextUtilities.verticesPerChar"/>
/// </remarks>
public Color32[] colors;
/// <summary>
/// A character's vertices positions.
/// </summary>
/// <remarks>
/// The array size is usually <see cref="TextUtilities.verticesPerChar"/>
/// </remarks>
public Vector3[] vertices;
/// <summary>
/// Related character information from TextMeshPro.<br/>
/// P.S. If you want to modify vertices/colors, please use the <see cref="vertices"/> and <see cref="colors"/> arrays variables instead.
/// </summary>
public TMPro.TMP_CharacterInfo tmp_CharInfo;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 941782baec821244b95c78c1e0958167
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
namespace Febucci.UI.Core
{
/// <summary>
/// Attribute used to set effect settings
/// </summary>
/// <example>
/// In order to set a "jump" tag to the below class:
/// <code>
/// [EffectInfo(tag: "jump")]
/// public class JumpEffect : BehaviorEffect
/// {
/// ///[...]
/// </code>
/// </example>
[System.AttributeUsage(System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class EffectInfoAttribute : System.Attribute
{
public readonly string tag;
public EffectInfoAttribute(string tag)
{
this.tag = tag;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f4cbdc4700ca58b43bb202055a604740
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,217 @@
namespace Febucci.UI.Core
{
/// <summary>
/// Base class for TextAnimator effects' categories<br/>
/// Please do not inherit from this class directly, but do from <see cref="AppearanceBase"/> or <see cref="BehaviorBase"/>
/// </summary>
public abstract class EffectsBase
{
/// <summary>
/// Effect's tag without symbols, eg. "shake"
/// </summary>
public string effectTag { get; private set; }
/// <summary>
/// Intensity used to uniform effects that behave differently based on screen or font sizes.
/// </summary>
/// <remarks>
/// Multiply this by your effect values only if they behave differently with different screen resolutions, font sizes or similar. (e.g. adding or subtracting vectors)
/// </remarks>
public float uniformIntensity = 1;
[System.Obsolete("This value will be removed from next versions. Please use 'uniformIntensity' instead")] public float effectIntensity => uniformIntensity;
#region Internal/Core
//---Methods used only by TextAnimator's internal workflow---//
internal class RegionManager
{
public string entireRichTextTag;
System.Collections.Generic.List<TextRegion> textRegions = new System.Collections.Generic.List<TextRegion>();
struct TextRegion
{
public int startIndex;
public int endIndex;
public TextRegion(int startIndex)
{
this.startIndex = startIndex;
this.endIndex = int.MaxValue;
}
}
internal bool IsLastRegionClosed()
{
return textRegions.Count > 0 && textRegions[textRegions.Count - 1].endIndex != int.MaxValue;
}
internal void AddRegion(int startIndex)
{
textRegions.Add(new TextRegion(startIndex));
}
internal bool TryReutilizingWithTag(string richTextTag, int indexNewRegionStart)
{
if (!entireRichTextTag.Equals(richTextTag))
return false;
if (!IsLastRegionClosed())
return true;
AddRegion(indexNewRegionStart);
return true;
}
internal void CloseEffect(int index)
{
var region = textRegions[textRegions.Count - 1];
region.endIndex = index;
textRegions[textRegions.Count - 1] = region;
}
internal bool IsCharInsideRegion(int charIndex)
{
foreach (var region in textRegions)
{
if (charIndex >= region.startIndex && charIndex < region.endIndex)
return true;
}
return false;
}
public override string ToString()
{
string text = $"{entireRichTextTag} - {textRegions.Count} region(s): ";
for (int i = 0; i < textRegions.Count; i++)
{
if(textRegions[i].endIndex == int.MaxValue)
{
text += $"[{textRegions[i].startIndex}; Infinity] ";
}
else
{
text += $"[{textRegions[i].startIndex}; {textRegions[i].endIndex}] ";
}
}
return text;
}
}
internal RegionManager regionManager;
/// <summary>
/// For internal use only. Sets the effect settings such as tags, instead of a constructor.
/// </summary>
/// <param name="effectTag"></param>
internal void _Initialize(string effectTag, string entireRichTextTag)
{
this.effectTag = effectTag;
this.regionManager = new RegionManager();
this.regionManager.entireRichTextTag = entireRichTextTag;
}
#endregion
#region Utilities
//---Methods you can use in your classes---//
/// <summary>
/// Applies the modifier by performing a multiplication to the given value.
/// </summary>
/// <param name="value">The effect's value you want to modify</param>
/// <param name="modifierValue">The modifier value. eg. "0.5"</param>
/// <example>
/// <code>
/// string modifier = "0.5";
/// float amplitude = 1;
/// ApplyModifierTo(ref amplitude, modifier);
/// //amplitude becomes 0.5
/// </code>
/// </example>
protected void ApplyModifierTo(ref float value, string modifierValue)
{
if (FormatUtils.ParseFloat(modifierValue, out float multiplier))
{
value *= multiplier;
}
}
#endregion
#region Effect Methods
//---Methods you can override in your classes---//
/// <summary>
/// Invoked upon effect creation
/// </summary>
/// <param name="charactersCount"></param>
public virtual void Initialize(int charactersCount) { }
/// <summary>
/// Called once per frame, before applying the effect to letters.
/// Example: You could use this to calculate the effect variables that are indiependant from specific letters
/// </summary>
public virtual void Calculate() { }
/// <summary>
/// Called once for each letter, per each frame.<br/>
/// Use this to apply the effect to a letter/character, by modifying its <see cref="CharacterData"/> values.
/// </summary>
/// <param name="data">Letters' values like position and colors. It might have been already modified by previous effects.</param>
/// <param name="charIndex">Letter index/position in the text.</param>
public abstract void ApplyEffect(ref CharacterData data, int charIndex);
/// <summary>
/// Invoked when there is a modifier in your rich text tag, eg. &#60;shake a=3&#62;
/// </summary>
/// <remarks>You can also use the following helper methods:
/// - <see cref="EffectsBase.ApplyModifierTo"/>
/// - <see cref="FormatUtils.ParseFloat"/>
/// </remarks>
/// <param name="modifierName">modifier name. eg. in &#60;shake a=3&#62; this string is "a"</param>
/// <param name="modifierValue">modifier value. eg. in &#60;shake a=3&#62; this string is "3"</param>
/// <example>
/// <code>
/// float amplitude = 2;
/// //[...]
/// public override void SetModifier(string modifierName, string modifierValue){
/// switch(modifierName){
/// //changes the 'amplitude' variable based on the modifier written in the tag
/// //eg. when you write a tag like &#60;shake a=3&#62;
/// case "a": ApplyModifierTo(ref amplitude, modifierValue); return;
/// }
/// }
/// </code>
/// </example>
public abstract void SetModifier(string modifierName, string modifierValue);
#if UNITY_EDITOR
//Used only in the editor to set again modifiers if we change values in the inspector
System.Collections.Generic.List<Modifier> modifiers { get; set; } = new System.Collections.Generic.List<Modifier>();
internal void EDITOR_RecordModifier(string name, string value)
{
modifiers.Add(new Modifier
{
name = name,
value = value,
});
}
internal void EDITOR_ApplyModifiers()
{
for (int i = 0; i < modifiers.Count; i++)
{
SetModifier(modifiers[i].name, modifiers[i].value);
}
}
#endif
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 26f45964032b7f64eb17e016c9b25e42
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
namespace Febucci.UI.Core
{
#if UNITY_EDITOR
struct Modifier
{
public string name;
public string value;
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2d03f0e128b12b3478b9445710bec25d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,128 @@
using UnityEngine;
using Febucci.Attributes;
namespace Febucci.UI.Core
{
[System.Serializable]
//Do not touch this script
public class AppearanceDefaultValues
{
#region Default Effects' values
private const float defDuration = .3f;
[System.Serializable]
public class Defaults
{
[PositiveValue] public float sizeDuration = defDuration;
[Attributes.MinValue(0)] public float sizeAmplitude = 2;
[PositiveValue] public float fadeDuration = defDuration;
[PositiveValue] public float verticalExpandDuration = defDuration;
public bool verticalFromBottom = false;
[PositiveValue] public float horizontalExpandDuration = defDuration;
[SerializeField] internal HorizontalExpandAppearance.ExpType horizontalExpandStart = HorizontalExpandAppearance.ExpType.Left;
[PositiveValue] public float diagonalExpandDuration = defDuration;
public bool diagonalFromBttmLeft = false;
[NotZero] public Vector2 offsetDir = Vector2.one;
[PositiveValue] public float offsetDuration = defDuration;
[NotZero] public float offsetAmplitude = 1f;
[PositiveValue] public float rotationDuration = defDuration;
public float rotationStartAngle = 180;
[PositiveValue] public float randomDirDuration = defDuration;
[NotZero] public float randomDirAmplitude = 1f;
}
[SerializeField, Header("Default Appearances")]
public Defaults defaults = new Defaults();
#endregion
[SerializeField, Header("Preset Effects")]
internal PresetAppearanceValues[] presets = new PresetAppearanceValues[0];
}
[System.Serializable]
//Do not touch this script
public class BehaviorDefaultValues
{
#region Default Effects' values
[System.Serializable]
public class Defaults
{
//wiggle
[NotZero] public float wiggleAmplitude = 0.15f;
[NotZero] public float wiggleFrequency = 7.67f;
//wave
[NotZero] public float waveFrequency = 4.78f;
[NotZero] public float waveAmplitude = .2f;
public float waveWaveSize = .18f;
//rot
[NotZero] public float angleSpeed = 180;
public float angleDiffBetweenChars = 10;
//swing
[NotZero] public float swingAmplitude = 27.5f;
[NotZero] public float swingFrequency = 5f;
public float swingWaveSize = 0;
//shake
[NotZero] public float shakeStrength = 0.085f;
[PositiveValue] public float shakeDelay = .04f;
//size
public float sizeAmplitude = 1.4f;
[NotZero] public float sizeFrequency = 4.84f;
public float sizeWaveSize = .18f;
//slide
[NotZero] public float slideAmplitude = 0.12f;
[NotZero] public float slideFrequency = 5;
public float slideWaveSize = 0;
//bounce
[NotZero] public float bounceAmplitude = .08f;
[NotZero] public float bounceFrequency = 1f;
public float bounceWaveSize = 0.08f;
//rainb
[NotZero] public float hueShiftSpeed = 0.8f;
public float hueShiftWaveSize = 0.08f;
//fade
[PositiveValue] public float fadeDelay = 1.2f;
//dangle
[NotZero] public float dangleAmplitude = .13f;
[NotZero] public float dangleFrequency = 2.41f;
public float dangleWaveSize = 0.18f;
public bool dangleAnchorBottom = false;
//pendulum
[NotZero] public float pendAmplitude = 25;
[NotZero] public float pendFrequency = 3;
public float pendWaveSize = .2f;
public bool pendInverted = false;
}
[SerializeField, Header("Default Behaviors")]
public Defaults defaults = new Defaults();
#endregion
[SerializeField, Header("Preset Effects")]
internal PresetBehaviorValues[] presets = new PresetBehaviorValues[0];
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d4926f3dbd96a4c4088cb85200cb67e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 35e8b95654a50e84593a284625387a04
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
namespace Febucci.UI.Core
{
struct EventMarker
{
public int charIndex;
public string eventMessage;
public bool triggered;
public int internalOrder;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 438f97e47f711044d9cf62066e9fc812
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,339 @@
using UnityEngine;
using System.Collections.Generic;
using System;
using System.Linq;
using System.Reflection;
namespace Febucci.UI.Core
{
#if UNITY_EDITOR
public static class TAnim_EditorHelper
{
internal delegate void VoidCallback();
internal static event VoidCallback onChangesApplied;
public static void TriggerEvent()
{
if (Application.isPlaying)
{
onChangesApplied?.Invoke();
}
}
}
#endif
public static class TAnimBuilder
{
[System.Serializable]
internal struct TagFormatting
{
public TagFormatting(char openingChar, char closingChar)
{
this.charOpeningTag = openingChar;
this.charClosingTag = closingChar;
}
public char charOpeningTag;
public char charClosingTag;
}
internal static TagFormatting tag_behaviors = new TagFormatting('<', '>');
internal static TagFormatting tag_appearances = new TagFormatting('{', '}');
static TAnimGlobalDataScriptable _data;
static bool hasData;
internal static TAnimGlobalDataScriptable data
{
get => _data;
}
#region Static Controller
static Dictionary<string, Type> behaviorsData = new Dictionary<string, Type>();
static Dictionary<string, Type> appearancesData = new Dictionary<string, Type>();
static HashSet<string> globalDefaultActions = new HashSet<string>();
static HashSet<string> globalCustomActions = new HashSet<string>();
static bool globalDatabaseInitialized;
public static string[] GetAllBehaviorsTags()
{
List<string> tags = new List<string>();
for (int i = 0; i < behaviorsData.Count; i++)
{
tags.Add(behaviorsData.Keys.ElementAt(i));
}
return tags.ToArray();
}
public static string[] GetAllApppearancesTags()
{
List<string> tags = new List<string>();
for (int i = 0; i < appearancesData.Count; i++)
{
tags.Add(appearancesData.Keys.ElementAt(i));
}
return tags.ToArray();
}
/// <summary>
/// Initializes and Load TextAnimator's effects and global settings, in case it has not been loaded already.
/// </summary>
public static void InitializeGlobalDatabase()
{
if (globalDatabaseInitialized)
return;
globalDatabaseInitialized = true;
TextUtilities.Initialize();
#region Local Methods
void PopulateEffectsFromAssembly<T>(ref Dictionary<string, Type> effectsList) where T : EffectsBase
{
List<Type> GetAssemblyClasses()
{
return (from domainAssembly in AppDomain.CurrentDomain.GetAssemblies()
from assemblyType in domainAssembly.GetTypes()
where assemblyType.IsSubclassOf(typeof(T))
where !assemblyType.IsAbstract
select assemblyType).ToList();
}
var effectsInAssembly = GetAssemblyClasses();
EffectInfoAttribute attribute;
string effectTag;
for (int i = 0; i < effectsInAssembly.Count; i++)
{
effectTag = string.Empty;
attribute = effectsInAssembly[i].GetCustomAttribute<EffectInfoAttribute>();
if (attribute != null)
{
effectTag = attribute.tag;
}
else
{
Debug.LogError($"TextAnimator: skipping class {effectsInAssembly[i].Name}. Please add a 'EffectInfoAttribute' on top of it.");
continue;
}
if (string.IsNullOrEmpty(effectTag))
{
continue;
}
if (!effectsList.ContainsKey(effectTag))
{
effectsList.Add(effectTag, effectsInAssembly[i]);
}
else
{
Debug.LogError($"TextAnimator: not adding effect <{effectTag}> (from class '{effectsInAssembly[i].Name}') to the database because an effect with the same tag has already been added (by class '{effectsList[effectTag].Name}')");
}
}
}
#endregion
PopulateEffectsFromAssembly<BehaviorBase>(ref behaviorsData);
PopulateEffectsFromAssembly<AppearanceBase>(ref appearancesData);
#region Default Actions
globalDefaultActions.Add("waitfor");
globalDefaultActions.Add("waitinput");
globalDefaultActions.Add("speed");
#endregion
hasData = false;
_data = Resources.Load(TAnimGlobalDataScriptable.resourcesPath) as TAnimGlobalDataScriptable;
if (data != null)
{
hasData = true;
#region Settings
if (data.customTagsFormatting)
{
if (data.tagInfo_behaviors.charOpeningTag != data.tagInfo_appearances.charOpeningTag
&& data.tagInfo_behaviors.charClosingTag != data.tagInfo_appearances.charClosingTag)
{
tag_behaviors = data.tagInfo_behaviors;
tag_appearances = data.tagInfo_appearances;
}
else
{
Debug.LogError("Not valid"); //todo error
}
}
#endregion
#region Global Effects
//Adds global effects
for (int i = 0; i < data.globalBehaviorPresets.Length; i++)
{
TryAddingPresetToDictionary(ref behaviorsData, data.globalBehaviorPresets[i].effectTag, typeof(PresetBehavior));
}
//Adds global effects
for (int i = 0; i < data.globalAppearancePresets.Length; i++)
{
TryAddingPresetToDictionary(ref appearancesData, data.globalAppearancePresets[i].effectTag, typeof(PresetAppearance));
}
#endregion
#region Custom Actions
if (data.customActions != null && data.customActions.Length > 0)
{
for (int i = 0; i < data.customActions.Length; i++)
{
if (data.customActions[i].Length <= 0)
{
Debug.LogError($"TextAnimator: Custom action {i} has an empty tag!");
continue;
}
if (globalCustomActions.Contains(data.customActions[i]))
{
Debug.LogError($"TextAnimator: Custom feature with tag '{data.customActions[i]}' is already present, it won't be added to the database.");
continue;
}
globalCustomActions.Add(data.customActions[i]);
}
}
#endregion
}
}
internal static bool TryGetGlobalPresetBehavior(string tag, out PresetBehaviorValues result)
{
if (!hasData) //avoids searching if data is null
{
result = default;
return false;
}
return GetPresetFromArray(tag, data.globalBehaviorPresets, out result);
}
internal static bool TryGetGlobalPresetAppearance(string tag, out PresetAppearanceValues result)
{
if (!hasData) //avoids searching if data is null
{
result = default;
return false;
}
return GetPresetFromArray(tag, data.globalAppearancePresets, out result);
}
internal static bool GetPresetFromArray<T>(string tag, T[] presets, out T result) where T : PresetBaseValues
{
if (presets.Length > 0)
{
for (int i = 0; i < presets.Length; i++)
{
if (tag.Equals(presets[i].effectTag))
{
result = presets[i];
return true;
}
}
}
result = default;
return false;
}
internal static bool IsDefaultAction(string tag)
{
if (globalDefaultActions.Count > 0 && globalDefaultActions.Contains(tag))
{
return true;
}
return false;
}
internal static bool IsCustomAction(string tag)
{
if (globalCustomActions.Count > 0 && globalCustomActions.Contains(tag))
{
return true;
}
return false;
}
internal static bool TryGetGlobalBehaviorFromTag(string effectTag, string entireRichTextTag, out BehaviorBase effectClass)
{
return TryGetEffectClassFromTag<BehaviorBase>(behaviorsData, effectTag, entireRichTextTag, out effectClass);
}
internal static bool TryGetGlobalAppearanceFromTag(string effectTag, string entireRichTextTag, out AppearanceBase effectClass)
{
return TryGetEffectClassFromTag(appearancesData, effectTag, entireRichTextTag, out effectClass);
}
internal static bool TryGetEffectClassFromTag<T>(Dictionary<string, Type> dictionary, string effectTag, string entireRichTextTag, out T effectClass) where T : EffectsBase
{
if (dictionary.ContainsKey(effectTag))
{
effectClass = Activator.CreateInstance(dictionary[effectTag]) as T;
effectClass._Initialize(effectTag, entireRichTextTag);
return true;
}
effectClass = default;
return false;
}
internal static void TryAddingPresetToDictionary(ref Dictionary<string, Type> database, string tag, Type type)
{
if (string.IsNullOrEmpty(tag))
{
Debug.LogWarning($"TextAnimator: Preset has a null or empty tag '{tag}'");
return;
}
if (!TextUtilities.IsTagLongEnough(tag))
{
Debug.LogWarning($"TextAnimator: Preset has tag '{tag}' shorter than three characters.");
return;
}
if (database.ContainsKey(tag))
{
Debug.LogWarning($"TextAnimator: A Preset has tag '{tag}' that's already present, it won't be added to the database.");
return;
}
database.Add(tag, type);
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 636e2806e423df447a5247629811c6a0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,35 @@
using UnityEngine;
using TagFormatting = Febucci.UI.Core.TAnimBuilder.TagFormatting;
namespace Febucci.UI.Core
{
/// <summary>
/// Stores TextAnimator's global data, shared in all your project (eg. Global Behaviors and Appearances).<br/>
/// Must be placed inside the Resources Path <see cref="resourcesPath"/><br/>
/// - Manual: <see href="https://www.febucci.com/text-animator-unity/docs/creating-effects-in-the-inspector/#global-effects">Creating Global Effects</see>
/// </summary>
[System.Serializable]
[CreateAssetMenu(fileName = "TextAnimator GlobalData", menuName = "TextAnimator/Create Global Text Animator Data")]
public class TAnimGlobalDataScriptable : ScriptableObject
{
/// <summary>
/// Resources Path where the scriptable object must be stored
/// </summary>
public const string resourcesPath = "TextAnimator GlobalData";
[SerializeField]
internal PresetBehaviorValues[] globalBehaviorPresets = new PresetBehaviorValues[0];
[SerializeField]
internal PresetAppearanceValues[] globalAppearancePresets = new PresetAppearanceValues[0];
[SerializeField]
internal string[] customActions = new string[0];
[SerializeField] internal bool customTagsFormatting = false;
[SerializeField] internal TagFormatting tagInfo_behaviors = new TagFormatting('<', '>');
[SerializeField] internal TagFormatting tagInfo_appearances = new TagFormatting('{', '}');
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a7bb413915491aa4d87d5f50a9bd1aaf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,717 @@
using System.Collections;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Events;
namespace Febucci.UI.Core
{
[System.Serializable]
public class CharacterEvent : UnityEvent<char> { }
/// <summary>
/// Base class for all TextAnimatorPlayers (typewriters). <br/>
/// - Manual: <see href="https://www.febucci.com/text-animator-unity/docs/text-animator-players/">TextAnimatorPlayers</see>.<br/>
/// </summary>
/// <remarks>
/// If you want to use the default TextAnimatorPlayer, see: <see cref="TextAnimatorPlayer"/><br/>
/// <br/>
/// You can also create custom typewriters by inheriting from this class. <br/>
/// Manual: <see href="https://www.febucci.com/text-animator-unity/docs/writing-custom-tanimplayers-c-sharp/">Writing Custom TextAnimatorPlayers (C#)</see>
/// </remarks>
[DisallowMultipleComponent]
[RequireComponent(typeof(TextAnimator))]
public abstract class TAnimPlayerBase : MonoBehaviour
{
[System.Flags]
enum StartTypewriterMode
{
/// <summary>
/// Typewriter starts typing ONLY if you invoke "StartShowingText" from any of your script.
/// </summary>
FromScriptOnly = 0,
/// <summary>
/// Typewriter automatically starts/resumes from the "OnEnable" method
/// </summary>
OnEnable = 1,
/// <summary>
/// Typewriter automatically starts once you call "ShowText" method [includes Easy Integration]
/// </summary>
OnShowText = 2,
AutomaticallyFromAllEvents = OnEnable | OnShowText //legacy support for unity 2018.x [instead of automatic recognition in 2019+]
}
#region Variables
#region Management Variables
string textToShow = string.Empty;
TextAnimator _textAnimator;
/// <summary>
/// The TextAnimator Component linked to this typewriter
/// </summary>
public TextAnimator textAnimator
{
get
{
if (_textAnimator != null)
return _textAnimator;
#if UNITY_2019_2_OR_NEWER
if(!TryGetComponent(out _textAnimator))
{
Debug.LogError($"TextAnimator: Text Animator component is null on GameObject {gameObject.name}");
}
#else
_textAnimator = GetComponent<TextAnimator>();
Assert.IsNotNull(_textAnimator, $"Text Animator component is null on GameObject {gameObject.name}");
#endif
return _textAnimator;
}
}
/// <summary>
/// <c>true</c> if the typewriter is currently showing letters.
/// </summary>
protected bool isBaseInsideRoutine => isInsideRoutine;
/// <summary>
/// <c>true</c> if the typewriter is waiting for the player input in the 'waitinput' action tag
/// </summary>
[HideInInspector] public bool isWaitingForPlayerInput { get; private set; }
bool isInsideRoutine = false;
bool isDisappearing = false;
/// <summary>
/// <c>true</c> if the player wants to skip the typewriter.<br/>
/// You can check/modify its value and also call <see cref="SkipTypewriter"/> to set it to <c>true</c>.
/// </summary>
/// <remarks>
/// P.S. It is reset back to <c>false</c> every time you show a new text.
/// </remarks>
protected bool wantsToSkip = false;
#endregion
#region Typewriter settings
/// <summary>
/// <c>true</c> if the typewriter is enabled
/// </summary>
[Tooltip("True if you want to shows the text dynamically")]
[SerializeField] public bool useTypeWriter = true;
[SerializeField, Tooltip("Controls from which method(s) the typewriter will automatically start/resume. Default is 'Automatic'")]
StartTypewriterMode startTypewriterMode = StartTypewriterMode.AutomaticallyFromAllEvents;
#region Typewriter Skip
[SerializeField]
bool canSkipTypewriter = true;
[SerializeField]
bool hideAppearancesOnSkip = false;
[SerializeField, Tooltip("True = plays all remaining events once the typewriter has been skipped")]
bool triggerEventsOnSkip = false;
#endregion
[SerializeField, Tooltip("True = resets the typewriter speed every time a new text is set/shown")] bool resetTypingSpeedAtStartup = true;
/// <summary>
/// Typewriter's speed (acts like a multiplier)<br/>
/// You can change this value or invoke <see cref="SetTypewriterSpeed(float)"/>
/// </summary>
protected float typewriterPlayerSpeed = 1;
public enum DisappearanceOrientation
{
SameAsTypewriter,
Inverted
}
[SerializeField] public DisappearanceOrientation disappearanceOrientation;
#endregion
#endregion
#region Events
/// <summary>
/// Called once the text is completely shown. <br/>
/// If the typewriter is enabled, this event is called once it has ended showing all letters.
/// </summary>
public UnityEvent onTextShowed;
/// <summary>
/// Called once the typewriter starts showing text.<br/>
/// It is only invoked when the typewriter is enabled.
/// </summary>
public UnityEvent onTypewriterStart;
/// <summary>
/// Callend once the typewriter has completed hiding all the letters.
/// </summary>
public UnityEvent onTextDisappeared;
/// <summary>
/// Called once a character has been shown by the typewriter.<br/>
/// It is only invoked when the typewriter is enabled.
/// </summary>
public CharacterEvent onCharacterVisible;
#endregion
/// <summary>
/// Shows remains letters dynamically
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
private IEnumerator ShowRemainingCharacters()
{
if (!textAnimator.allLettersShown)
{
isInsideRoutine = true;
isWaitingForPlayerInput = false;
isDisappearing = false;
wantsToSkip = false;
onTypewriterStart?.Invoke();
IEnumerator WaitTime(float time)
{
if (time > 0)
{
float t = 0;
while (t <= time && !HasSkipped())
{
t += textAnimator.time.deltaTime;
yield return null;
}
}
}
float timeToWait;
char characterShown;
if (resetTypingSpeedAtStartup)
typewriterPlayerSpeed = 1;
float typewriterTagsSpeed = 1;
bool HasSkipped()
{
return canSkipTypewriter && wantsToSkip;
}
float timePassed = 0;
float deltaTime;
UpdateDeltaTime();
void UpdateDeltaTime()
{
deltaTime = textAnimator.time.deltaTime * typewriterPlayerSpeed * typewriterTagsSpeed;
}
//Shows character by character until all are shown
while (!textAnimator.allLettersShown)
{
//searches for actions [before the character, incl. at the very start of the text]
if (textAnimator.hasActions)
{
//loops until features ended (there could be multiple ones in the same text position, example: when two tags are next to eachother without spaces
while (textAnimator.TryGetAction(out TypewriterAction action))
{
//Default features
switch (action.actionID)
{
case "waitfor":
float waitTime;
FormatUtils.TryGetFloat(action.parameters, 0, 1f, out waitTime);
yield return WaitTime(waitTime);
break;
case "waitinput":
isWaitingForPlayerInput = true;
yield return WaitInput();
isWaitingForPlayerInput = false;
break;
case "speed":
FormatUtils.TryGetFloat(action.parameters, 0, 1, out typewriterTagsSpeed);
//clamps speed (time cannot go backwards!)
if (typewriterTagsSpeed <= 0)
{
typewriterTagsSpeed = 0.001f;
}
break;
//Action is custom
default:
yield return DoCustomAction(action);
break;
}
}
}
//increases the visible chars count
textAnimator.maxVisibleCharacters++;
textAnimator.TriggerVisibleEvents();
characterShown = textAnimator.latestCharacterShown.character;
UpdateDeltaTime();
//triggers event unless it's a space
if (characterShown != ' ')
{
onCharacterVisible?.Invoke(characterShown);
}
//gets the time to wait based on the newly character showed
timeToWait = GetWaitAppearanceTimeOf(characterShown);
//waiting less time than a frame, we don't wait yet
if (timeToWait < deltaTime)
{
timePassed += timeToWait;
if (timePassed >= deltaTime) //waits only if we "surpassed" a frame duration
{
yield return null;
timePassed %= deltaTime;
}
}
else
{
while (timePassed < timeToWait && !HasSkipped())
{
OnTypewriterCharDelay();
timePassed += deltaTime;
yield return null;
UpdateDeltaTime();
}
timePassed %= timeToWait;
}
//Skips typewriter
if (HasSkipped())
{
textAnimator.ShowAllCharacters(hideAppearancesOnSkip);
if (triggerEventsOnSkip)
{
textAnimator.TriggerRemainingEvents();
}
break;
}
}
// triggers the events at the end of the text
//
// the typewriter is arrived here without skipping
// meaning that all events were triggered and we only
// have to fire the ones at the very end
// (outside tmpro's characters count length)
if (!canSkipTypewriter || !wantsToSkip)
{
textAnimator.TriggerRemainingEvents();
}
isInsideRoutine = false;
isWaitingForPlayerInput = false;
textToShow = string.Empty; //text has been showed, no need to store it now
onTextShowed?.Invoke();
}
}
#region Public Methods
/// <summary>
/// Sets the TextAnimator text. If enabled, it also starts showing letters dynamically. <br/>
/// - Manual: <see href="https://www.febucci.com/text-animator-unity/docs/text-animator-players/">Text Animator Players</see>
/// </summary>
/// <param name="text"></param>
/// <remarks>
/// If the typewriter is enabled but its start mode (editable in the Inspector) doesn't include <see cref="StartTypewriterMode.OnShowText"/>, this method won't start showing letters. You'd have to manually call <see cref="StartShowingText"/> in order to start the typewriter, or include different "start modes" like <see cref="StartTypewriterMode.OnEnable"/> and let the script manage it automatically.
/// </remarks>
public void ShowText(string text)
{
StopShowingText();
if (string.IsNullOrEmpty(text))
{
textToShow = string.Empty;
textAnimator.SetText(string.Empty, true);
return;
}
textToShow = text;
isWaitingForPlayerInput = false;
wantsToSkip = false;
textAnimator.SetText(textToShow, useTypeWriter);
textAnimator.firstVisibleCharacter = 0;
isDisappearing = false;
if (!useTypeWriter)
{
onTextShowed?.Invoke();
}
else
{
if (startTypewriterMode.HasFlag(StartTypewriterMode.OnShowText))
{
StartShowingText();
}
}
}
#region Typewriter
bool CanStartAnyCoroutine()
{
#if UNITY_EDITOR
if (!Application.isPlaying) //prevents from firing in edit mode from the context menu
return false;
#endif
if (!gameObject.activeInHierarchy)
{
Debug.LogWarning("TextAnimator: couldn't start coroutine because the gameobject is not active");
return false;
}
return true;
}
#region Appearing
/// <summary>
/// Starts showing letters dynamically
/// </summary>
/// <param name="resetVisibleCharacters"><code>false</code> if you want the typewriter to resume where it was left. <code>true</code> if the typewriter should restart from character 0</param>
public void StartShowingText(bool resetVisibleCharacters = false)
{
if (!useTypeWriter)
{
Debug.LogWarning("TextAnimator: couldn't start coroutine because 'useTypewriter' is disabled");
return;
}
if (!CanStartAnyCoroutine()) return;
if (resetVisibleCharacters)
{
textAnimator.firstVisibleCharacter = 0;
textAnimator.maxVisibleCharacters = 0;
}
if (!isInsideRoutine) //starts only if it is not already typing
{
StartCoroutine(ShowRemainingCharacters());
}
}
[ContextMenu("Skip Typewriter")]
/// <summary>
/// Skips the typewriter animation (if it's currently showing)
/// </summary>
public void SkipTypewriter()
{
#if UNITY_EDITOR
if (!Application.isPlaying) //prevents from firing in edit mode from the context menu
return;
#endif
wantsToSkip = true;
}
/// <summary>
/// Stops showing letters dynamically, leaving the text as it is.
/// </summary>
[ContextMenu("Stop Showing Text")]
public void StopShowingText()
{
#if UNITY_EDITOR
if (!Application.isPlaying) //prevents from firing in edit mode from the context menu
return;
#endif
//Stops only if we're inside the routine
if (isInsideRoutine)
{
isInsideRoutine = false;
StopAllCoroutines();
}
textToShow = string.Empty;
}
#endregion
#region Disappearing
/// <summary>
/// Starts disappearing the text dynamically
/// </summary>
[ContextMenu("Start Disappearing Text")]
public void StartDisappearingText()
{
if (!CanStartAnyCoroutine()) return;
if (disappearanceOrientation == DisappearanceOrientation.Inverted && isInsideRoutine)
{
Debug.LogWarning("TextAnimatorPlayer: Can't start disappearance routine in the opposite direction of the typewriter, because you're still showing the text! (the typewriter might get stuck trying to show and override letters that keep disappearing)");
return;
}
StartCoroutine(DisappearRoutine());
}
IEnumerator DisappearRoutine()
{
isDisappearing = true;
float t = 0;
float deltaTime = 0;
void UpdateDeltaTime()
{
deltaTime = textAnimator.time.deltaTime * typewriterPlayerSpeed;
}
UpdateDeltaTime();
bool CanDisappear() => isDisappearing && textAnimator.firstVisibleCharacter <= textAnimator.maxVisibleCharacters && textAnimator.maxVisibleCharacters > 0;
IEnumerator WaitFor(float timeToWait)
{
if (timeToWait <= 0)
yield break;
while (t < timeToWait) //waits
{
t += deltaTime;
yield return null;
UpdateDeltaTime();
}
t %= timeToWait;
}
if (disappearanceOrientation == DisappearanceOrientation.SameAsTypewriter)
{
var textInfo = textAnimator.tmproText.textInfo;
int charCount = textInfo.characterCount;
var charInfo = textInfo.characterInfo;
while (CanDisappear() && textAnimator.firstVisibleCharacter<charCount)
{
textAnimator.firstVisibleCharacter++;
float timeToWait = GetWaitDisappearanceTimeOf(charInfo[textAnimator.firstVisibleCharacter - 1].character);
//waiting less time than a frame, we don't wait yet
if (timeToWait < deltaTime)
{
t += timeToWait;
if (t >= deltaTime) //waits only if we "surpassed" a frame duration
{
yield return null;
t %= deltaTime;
}
}
else
yield return WaitFor(timeToWait);
}
}
else
{
while (CanDisappear())
{
textAnimator.maxVisibleCharacters--;
float timeToWait = GetWaitDisappearanceTimeOf(textAnimator.latestCharacterShown.character);
//waiting less time than a frame, we don't wait yet
if (timeToWait < deltaTime)
{
t += timeToWait;
if (t >= deltaTime) //waits only if we "surpassed" a frame duration
{
yield return null;
t %= deltaTime;
}
}
else yield return WaitFor(timeToWait);
}
}
//Waits until all letters are completely hidden/disappeared
while (textAnimator.anyLetterVisible)
yield return null;
//Fires the event if the entire text has been hidden (so, this method has not been interrupted)
if ((textAnimator.firstVisibleCharacter >= textAnimator.maxVisibleCharacters && textAnimator.allLettersShown) || textAnimator.maxVisibleCharacters == 0)
{
onTextDisappeared.Invoke();
}
isDisappearing = false;
}
/// <summary>
/// Stops the typewriter's from disappearing the text dynamically, leaving the text at its current state
/// </summary>
[ContextMenu("Stop Disappearing Text")]
public void StopDisappearingText()
{
isDisappearing = false;
}
#endregion
/// <summary>
/// Makes the typewriter slower/faster, by setting its internal speed multiplier.
/// </summary>
/// <param name="value"></param>
/// <example>
/// If the typewriter has to wait <c>1</c> second to show the next letter but you set the typewriter speed to <c>2</c>, the typewriter will wait <c>0.5</c> seconds.
/// </example>
/// <remarks>
/// The minimum value is 0.001
/// </remarks>
public void SetTypewriterSpeed(float value)
{
typewriterPlayerSpeed = Mathf.Clamp(value, .001f, value);
}
#endregion
#endregion
#region Virtual/Abstract Methods
/// <summary>
/// Waits for user input in order to continue showing text. Invoked when there is a waitinput action tag (Manual: <see href="http://localhost:4000/docs/performing-actions-while-typing/">Performing Actions while Typing</see>)
/// </summary>
/// <returns></returns>
/// <remarks>
/// You can customize this based on your project inputs.
/// </remarks>
protected abstract IEnumerator WaitInput();
/// <summary>
/// Returns the typewriter's appearance waiting time based on a given character/letter.
/// </summary>
/// <param name="character"></param>
/// <returns></returns>
/// <remarks>
/// You can customize this in your custom typewriter for your game.<br/>
/// Some variables/methods that you could use here:<br/>
/// - <seealso cref="TextAnimator.latestCharacterShown"/><br/>
/// - <seealso cref="TextAnimator.TryGetNextCharacter(out TMPro.TMP_CharacterInfo)"/><br/>
/// </remarks>
/// <example>
/// Waiting more time if the character is puntuaction.
/// <code>
/// protected override float WaitTimeOf(char character)
/// {
/// if (char.IsPunctuation(character))
/// return .06f;
///
/// return .03f;
/// }
/// </code>
/// </example>
protected abstract float GetWaitAppearanceTimeOf(char character);
///from previous versions
[System.Obsolete("'WaitTimeOf' is obsolete and will be removed from the next versions. Pleaase use 'GetWaitAppearanceTimeOf' instead.")]
protected virtual void WaitTimeOf(char character) => GetWaitAppearanceTimeOf(character);
/// <summary>
/// Returns the typewriter's disappearance waiting time based on a given character/letter.
/// </summary>
/// <param name="character"></param>
/// <returns></returns>
/// <remarks>
/// You can customize this in your custom typewriter for your game.<br/>
/// </remarks>
protected virtual float GetWaitDisappearanceTimeOf(char character) => GetWaitAppearanceTimeOf(character);
/// <summary>
/// Override this method in order to implement custom actions in your typewriter.<br/>
/// - Manual: <see href="https://www.febucci.com/text-animator-unity/docs/writing-custom-actions-c-sharp/">Writing Custom Actions C#</see>
/// </summary>
/// <param name="action"></param>
/// <returns></returns>
protected virtual IEnumerator DoCustomAction(TypewriterAction action)
{
throw new System.NotImplementedException($"TextAnimator: Custom Action not implemented with type: {action.actionID}. If you did implement it, please do not call the base method from your overridden one.");
}
/// <summary>
/// Invoked for every frame the typewriter is waiting to show the next letter.<br/>
/// </summary>
/// <remarks>
/// You could use this in order to speed up the waiting time based on the player input. <br/>
/// - See: <see cref="SetTypewriterSpeed(float)"/>
/// </remarks>
protected virtual void OnTypewriterCharDelay()
{
}
#endregion
/// <summary>
/// Unity's default MonoBehavior 'OnDisable' callback.
/// </summary>
/// <remarks>
/// P.S. If you're overriding this method, don't forget to invoke the base one.
/// </remarks>
protected virtual void OnDisable()
{
isInsideRoutine = false;
}
/// <summary>
/// Unity's default MonoBehavior 'OnEnable' callback.
/// </summary>
/// <remarks>
/// P.S. If you're overriding this method, don't forget to invoke the base one.
/// </remarks>
protected virtual void OnEnable()
{
if (!useTypeWriter)
return;
if (!startTypewriterMode.HasFlag(StartTypewriterMode.OnEnable))
return;
StartShowingText();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ac6b0a2b8ddbab342ad54b5409ccffec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
namespace Febucci.UI
{
/// <summary>
/// Contains all the tags for built-in effects.<br/>
/// - Manual: <seealso href="https://www.febucci.com/text-animator-unity/docs/built-in-effects-list/">Built-in Effects</seealso>
/// </summary>
public static class TAnimTags
{
#region Tags
public const string bh_Shake = "shake";
public const string bh_Rot = "rot";
public const string bh_Wiggle = "wiggle";
public const string bh_Wave = "wave";
public const string bh_Swing = "swing";
public const string bh_Incr = "incr";
public const string bh_Slide = "slide";
public const string bh_Bounce = "bounce";
public const string bh_Fade = "fade";
public const string bh_Rainb = "rainb";
public const string bh_Dangle = "dangle";
public const string bh_Pendulum = "pend";
public const string ap_Size = "size";
public const string ap_Fade = "fade";
public const string ap_Offset = "offset";
public const string ap_RandomDir = "rdir";
public const string ap_VertExp = "vertexp";
public const string ap_HoriExp = "horiexp";
public const string ap_DiagExp = "diagexp";
public const string ap_Rot = "rot";
#endregion
/// <summary>
/// Contains all default behavior effects tags
/// </summary>
public static readonly string[] defaultBehaviors = new string[]
{
bh_Shake,
bh_Rot,
bh_Wiggle,
bh_Wave,
bh_Swing,
bh_Incr,
bh_Slide,
bh_Bounce,
bh_Fade,
bh_Rainb,
bh_Dangle,
bh_Pendulum
};
/// <summary>
/// Contains all default appearance effects tags
/// </summary>
public static readonly string[] defaultAppearances = new string[]{
ap_Size,
ap_Fade,
ap_Offset,
ap_VertExp,
ap_HoriExp,
ap_DiagExp,
ap_Rot,
ap_RandomDir
};
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0527782f4a0130c469e55649e2c419e1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
namespace Febucci.UI
{
/// <summary>
/// TextAnimator's typewriter action
/// </summary>
public struct TypewriterAction
{
/// <summary>
/// ID of the action without the tag symbols, eg. 'waitfor'
/// </summary>
public string actionID;
/// <summary>
/// Contains all the parameters passed via the rich text tag
/// </summary>
/// <example>
/// If you write <waitfor=5> in the text, the following code will result in:
/// <code>
/// float waitTime;
/// Febucci.UI.Core.FormatUtils.TryGetFloat(action.parameters, 0, 1f, out waitTime);
/// //waitTime is now 5
/// </code>
/// </example>
/// <see cref="Core.FormatUtils.ParseFloat(string, out float)"/>
public List<string> parameters;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 85b02a7fdec0a8840b184b90beaed9c7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 68a955ba748c0894588a272f4b7b1aa7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9c46d0e3b4076cf4395612be87694635
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fc174d1751ee25242a118f987b8f9f66
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
namespace Febucci.UI.Core
{
/// <summary>
/// Base class for all appearance effects. <br/>
/// Inherit from this class if you want to create your own Appearance Effect in C#.
/// </summary>
public abstract class AppearanceBase : EffectsBase
{
public float effectDuration = .3f;
[System.Obsolete("This variable will be removed from next versions. Please use 'effectDuration' instead")]
protected float showDuration => effectDuration;
/// <summary>
/// Initializes the effect's default values. It is called before the effect is applied to letters.
/// </summary>
/// <remarks>
/// Use this to assign values to your effect.
/// </remarks>
/// <example>
/// <code>
/// effectDuration = data.defaults.sizeDuration;
/// amplitude = data.defaults.sizeAmplitude;
/// </code>
/// </example>
/// <param name="data"></param>
public abstract void SetDefaultValues(AppearanceDefaultValues data);
public virtual bool CanShowAppearanceOn(float timePassed)
{
return timePassed <= effectDuration;
}
public override void SetModifier(string modifierName, string modifierValue)
{
switch (modifierName)
{
//duration
case "d": ApplyModifierTo(ref effectDuration, modifierValue); break;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5168d9d1fb0c81d45a9c771529d45e4f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 41f2670940e258c4ca5bcac3643b6179
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,57 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.ap_DiagExp)]
class DiagonalExpandAppearance : AppearanceBase
{
int targetA;
int targetB;
//--Temp variables--
Vector3 middlePos;
float pct;
public override void SetDefaultValues(AppearanceDefaultValues data)
{
effectDuration = data.defaults.diagonalExpandDuration;
SetOrientation(data.defaults.diagonalFromBttmLeft);
}
void SetOrientation(bool btmLeft)
{
if (btmLeft) //expands bottom left and top right
{
targetA = 0;
targetB = 2;
}
else //expands bottom right and top left
{
targetA = 1;
targetB = 3;
}
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
middlePos = data.vertices.GetMiddlePos();
pct = Tween.EaseInOut(data.passedTime / effectDuration);
data.vertices[targetA] = Vector3.LerpUnclamped(middlePos, data.vertices[targetA], pct);
//top right copies from bottom right
data.vertices[targetB] = Vector3.LerpUnclamped(middlePos, data.vertices[targetB], pct);
}
public override void SetModifier(string modifierName, string modifierValue)
{
base.SetModifier(modifierName, modifierValue);
switch (modifierName)
{
case "bot": SetOrientation(modifierValue == "1"); break;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7303246fad8e2a049aebd6c0c71d2985
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.ap_Fade)]
class FadeAppearance : AppearanceBase
{
public override void SetDefaultValues(AppearanceDefaultValues data)
{
effectDuration = data.defaults.fadeDuration;
}
Color32 temp;
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
//from transparent to real color
for (int i = 0; i < TextUtilities.verticesPerChar; i++)
{
temp = data.colors[i];
temp.a = 0;
data.colors[i] = Color32.LerpUnclamped(data.colors[i], temp,
Tween.EaseInOut(1 - (data.passedTime / effectDuration)));
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d26528e73e36f64dab95b69625232a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,94 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.ap_HoriExp)]
class HorizontalExpandAppearance : AppearanceBase
{
//expand type
public enum ExpType
{
Left, //from left to right
Middle, //expands from the middle to te extents
Right //from right to left
}
ExpType type = ExpType.Left;
//--Temp variables--
Vector2 startTop;
Vector2 startBot;
float pct;
public override void SetDefaultValues(AppearanceDefaultValues data)
{
effectDuration = data.defaults.horizontalExpandDuration;
type = data.defaults.horizontalExpandStart;
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
pct = Tween.EaseInOut(data.passedTime / effectDuration);
switch (type)
{
default:
case ExpType.Left:
//top left and bot left
startTop = data.vertices[1];
startBot = data.vertices[0];
data.vertices[2] = Vector3.LerpUnclamped(startTop, data.vertices[2], pct);
data.vertices[3] = Vector3.LerpUnclamped(startBot, data.vertices[3], pct);
break;
case ExpType.Right:
//top right and bot right
startTop = data.vertices[2];
startBot = data.vertices[3];
data.vertices[1] = Vector3.LerpUnclamped(startTop, data.vertices[1], pct);
data.vertices[0] = Vector3.LerpUnclamped(startBot, data.vertices[0], pct);
break;
case ExpType.Middle:
//Middle positions
startTop = (data.vertices[1] + data.vertices[2]) / 2;
startBot = (data.vertices[0] + data.vertices[3]) / 2;
//top vertices
data.vertices[1] = Vector3.LerpUnclamped(startTop, data.vertices[1], pct);
data.vertices[2] = Vector3.LerpUnclamped(startTop, data.vertices[2], pct);
//bottom vertices
data.vertices[0] = Vector3.LerpUnclamped(startBot, data.vertices[0], pct);
data.vertices[3] = Vector3.LerpUnclamped(startBot, data.vertices[3], pct);
break;
}
}
public override void SetModifier(string modifierName, string modifierValue)
{
base.SetModifier(modifierName, modifierValue);
switch (modifierName)
{
case "x":
switch (modifierValue)
{
case "-1": type = ExpType.Left; break;
case "0": type = ExpType.Middle; break;
case "1": type = ExpType.Right; break;
default: Debug.LogError($"Text Animator: you set an '{modifierName}' modifier with value '{modifierValue}' for the HorizontalExpandAppearance effect, but it can only be '-1', '0', or '1'"); break;
}
break;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3dbde6098d9bea441b3ac872e52073ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.ap_Offset)]
class OffsetAppearance : AppearanceBase
{
float amount;
Vector2 direction;
public override void SetDefaultValues(AppearanceDefaultValues data)
{
direction = data.defaults.offsetDir;
amount = data.defaults.offsetAmplitude * uniformIntensity;
effectDuration = data.defaults.offsetDuration;
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
//Moves all towards a direction
data.vertices.MoveChar(direction * amount * Tween.EaseIn(1 - data.passedTime / effectDuration));
}
public override void SetModifier(string modifierName, string modifierValue)
{
base.SetModifier(modifierName, modifierValue);
switch (modifierName)
{
case "a": ApplyModifierTo(ref amount, modifierValue); break;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 146552007a93ecc42b0d63f26788e6cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.ap_RandomDir)]
class RandomDirectionAppearance : AppearanceBase
{
float amount;
Vector3[] directions;
public override void Initialize(int charactersCount)
{
base.Initialize(charactersCount);
directions = new Vector3[charactersCount];
//Calculates a random direction for each character (which won't change)
for(int i = 0; i < charactersCount; i++)
{
directions[i] = TextUtilities.fakeRandoms[Random.Range(0, TextUtilities.fakeRandomsCount - 1)] * Mathf.Sign(Mathf.Sin(i));
}
}
public override void SetDefaultValues(AppearanceDefaultValues data)
{
amount = data.defaults.randomDirAmplitude;
effectDuration = data.defaults.randomDirDuration;
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
//Moves all towards a direction
data.vertices.MoveChar(directions[charIndex] * amount * uniformIntensity * Tween.EaseIn(1 - data.passedTime / effectDuration));
}
public override void SetModifier(string modifierName, string modifierValue)
{
base.SetModifier(modifierName, modifierValue);
switch (modifierName)
{
case "a": ApplyModifierTo(ref amount, modifierValue); break;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6a65a9b7775dded41a0295e6f75be25d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.ap_Rot)]
class RotatingAppearance : AppearanceBase
{
float targetAngle;
public override void SetDefaultValues(AppearanceDefaultValues data)
{
effectDuration = data.defaults.rotationDuration;
targetAngle = data.defaults.rotationStartAngle;
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
data.vertices.RotateChar(
Mathf.Lerp(
targetAngle,
0,
Tween.EaseInOut(data.passedTime / effectDuration)
)
);
}
public override void SetModifier(string modifierName, string modifierValue)
{
base.SetModifier(modifierName, modifierValue);
switch (modifierName)
{
case "a": ApplyModifierTo(ref targetAngle, modifierValue); break;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 465a2951870a6954d8205f9c58a546de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.ap_Size)]
class SizeAppearance : AppearanceBase
{
float amplitude;
public override void SetDefaultValues(AppearanceDefaultValues data)
{
effectDuration = data.defaults.sizeDuration;
amplitude = data.defaults.sizeAmplitude * -1 + 1;
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
data.vertices.LerpUnclamped(
data.vertices.GetMiddlePos(),
Tween.EaseIn(1 - (data.passedTime / effectDuration)) * amplitude
);
}
public override void SetModifier(string modifierName, string modifierValue)
{
base.SetModifier(modifierName, modifierValue);
switch (modifierName)
{
case "a": ApplyModifierTo(ref amplitude, modifierValue); break;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5ff5882c38f1b6c4682c7f207f0c98db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.ap_VertExp)]
class VerticalExpandAppearance : AppearanceBase
{
int startA, targetA;
int startB, targetB;
//--Temp variables--
float pct;
public override void SetDefaultValues(AppearanceDefaultValues data)
{
effectDuration = data.defaults.verticalExpandDuration;
SetOrientation(data.defaults.verticalFromBottom);
}
void SetOrientation(bool fromBottom)
{
if (fromBottom) //From bottom to top
{
//top left copies bottom left
startA = 0;
targetA = 1;
//top right copies bottom right
startB = 3;
targetB = 2;
}
else //from top to bottom
{
//bottom left copies top left
startA = 1;
targetA = 0;
//bottom right copies top right
startB = 2;
targetB = 3;
}
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
pct = Tween.EaseInOut(data.passedTime / effectDuration);
data.vertices[targetA] = Vector3.LerpUnclamped(data.vertices[startA], data.vertices[targetA], pct);
data.vertices[targetB] = Vector3.LerpUnclamped(data.vertices[startB], data.vertices[targetB], pct);
}
public override void SetModifier(string modifierName, string modifierValue)
{
base.SetModifier(modifierName, modifierValue);
switch (modifierName)
{
case "bot": SetOrientation(modifierValue == "1"); break;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 351d9d9e5c9ea7946842aee1160f7731
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: efe369a01f5ffe54583002264aa47ea7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,206 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: "")]
class PresetAppearance : AppearanceBase
{
bool enabled;
//management
Matrix4x4 matrix;
Vector3 offset;
Quaternion rotationQua;
bool hasTransformEffects;
ThreeAxisEffector movement;
ThreeAxisEffector rotation;
TwoAxisEffector scale;
bool setColor;
Color32 color;
ColorCurve colorCurve;
public override void SetDefaultValues(AppearanceDefaultValues data)
{
effectDuration = 0;
enabled = false;
void AssignValues(PresetAppearanceValues result)
{
enabled = SetPreset(
true,
result,
ref movement,
ref effectDuration,
ref rotation,
ref scale,
ref rotationQua,
ref hasTransformEffects,
ref setColor,
ref colorCurve);
}
PresetAppearanceValues values;
//searches for local presets first, which override global presets
if (TAnimBuilder.GetPresetFromArray(effectTag, data.presets, out values))
{
AssignValues(values);
return;
}
//global presets
if (TAnimBuilder.TryGetGlobalPresetAppearance(effectTag, out values))
{
AssignValues(values);
return;
}
}
#region Effector classes
internal abstract class Effector
{
protected abstract Vector3 _EvaluateEffect(float passedTime, int charInde);
public Vector3 EvaluateEffect(float passedTime, int charIndex)
{
return _EvaluateEffect(passedTime, charIndex);
}
}
internal sealed class ThreeAxisEffector : Effector
{
EffectEvaluator x;
EffectEvaluator y;
EffectEvaluator z;
public ThreeAxisEffector(EffectEvaluator x, EffectEvaluator y, EffectEvaluator z)
{
this.x = x;
this.y = y;
this.z = z;
}
protected override Vector3 _EvaluateEffect(float passedTime, int charIndex)
{
return new Vector3(
x.Evaluate(passedTime, charIndex),
y.Evaluate(passedTime, charIndex),
z.Evaluate(passedTime, charIndex)
);
}
}
internal sealed class TwoAxisEffector : Effector
{
EffectEvaluator x;
EffectEvaluator y;
public TwoAxisEffector(EffectEvaluator x, EffectEvaluator y)
{
this.x = x;
this.y = y;
}
protected override Vector3 _EvaluateEffect(float passedTime, int charIndex)
{
return new Vector3(
x.Evaluate(passedTime, charIndex),
y.Evaluate(passedTime, charIndex),
1
);
}
}
#endregion
public static bool SetPreset<T>(
bool isAppearance,
T values,
ref ThreeAxisEffector movement,
ref float showDuration,
ref ThreeAxisEffector rotation,
ref TwoAxisEffector scale,
ref Quaternion rotationQua,
ref bool hasTransformEffects,
ref bool setColor,
ref ColorCurve colorCurve
) where T : PresetBaseValues
{
values.Initialize(isAppearance);
showDuration = values.GetMaxDuration();
movement = new ThreeAxisEffector(
values.movementX,
values.movementY,
values.movementZ);
scale = new TwoAxisEffector(
values.scaleX,
values.scaleY
);
rotation = new ThreeAxisEffector(
values.rotX,
values.rotY,
values.rotZ
);
rotationQua = Quaternion.identity;
hasTransformEffects = values.movementX.enabled || values.movementY.enabled || values.movementZ.enabled
|| values.rotX.enabled || values.rotY.enabled || values.rotZ.enabled
|| values.scaleX.enabled || values.scaleY.enabled;
setColor = values.color.enabled;
if (setColor)
{
colorCurve = values.color;
colorCurve.Initialize(isAppearance);
}
return hasTransformEffects || setColor;
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
if (!enabled)
return;
if (hasTransformEffects)
{
offset = (data.vertices[0] + data.vertices[2]) / 2f;
rotationQua.eulerAngles = rotation.EvaluateEffect(data.passedTime, charIndex);
matrix.SetTRS(
movement.EvaluateEffect(data.passedTime, charIndex) * uniformIntensity,
rotationQua,
scale.EvaluateEffect(data.passedTime, charIndex));
for (byte i = 0; i < data.vertices.Length; i++)
{
data.vertices[i] -= offset;
data.vertices[i] = matrix.MultiplyPoint3x4(data.vertices[i]);
data.vertices[i] += offset;
}
}
if (setColor)
{
color = colorCurve.GetColor(data.passedTime, charIndex);
data.colors.LerpUnclamped(color, 1 - data.passedTime / effectDuration);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 36ad6bdd3b4d27947bf1042b1aed96c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,13 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[System.Serializable]
internal class PresetAppearanceValues : PresetBaseValues
{
public PresetAppearanceValues() : base()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fff0e4bcc82c4e44cbb984e75da9a96b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 33a9b3141fa81fc40a97f74e04a25ab5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4b35db61dfff3d843ac76bc4f9b2669b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
namespace Febucci.UI.Core
{
/// <summary>
/// Base class for all behavior effects.<br/>
/// Inherit from this class if you want to create your own Behavior Effect in C#.
/// </summary>
public abstract class BehaviorBase : EffectsBase
{
public abstract void SetDefaultValues(BehaviorDefaultValues data);
[System.Obsolete("This variable will be removed from next versions. Please use 'time.timeSinceStart' instead")]
public float animatorTime => time.timeSinceStart;
[System.Obsolete("This variable will be removed from next versions. Please use 'time.deltaTime' instead")]
public float animatorDeltaTime => time.deltaTime;
/// <summary>
/// Contains data/settings from the TextAnimator component that is linked to (and managing) this effect.
/// </summary>
public TextAnimator.TimeData time { get; private set; }
internal void SetAnimatorData(in TextAnimator.TimeData time)
{
this.time = time;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9ded2259c692766478d078fed5b5da3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,39 @@
using UnityEngine;
namespace Febucci.UI.Core
{
/// <summary>
/// Behavior helper class that automatically manages the following modifiers: (a = <see cref="amplitude"/>), (f = <see cref="frequency"/>) and (w = <see cref="waveSize"/>).<br/><br/>
/// You can inerith from this class and use the modifiers as you prefer in your effects, without having to set up them inside the <see cref="SetModifier(string, string)"/> method.
/// </summary>
/// <example>
/// All the TextAnimator effects that have 3 modifiers inerith from this class. You can check their source code to see how they are set up, example: <see cref="WiggleBehavior"/> or <see cref="WaveBehavior"/>
/// </example>
public abstract class BehaviorSine : BehaviorBase
{
protected float amplitude = 1;
protected float frequency = 1;
protected float waveSize = .08f;
public override void SetModifier(string modifierName, string modifierValue)
{
switch (modifierName)
{
//amplitude
case "a": ApplyModifierTo(ref amplitude, modifierValue); break;
//frequency
case "f": ApplyModifierTo(ref frequency, modifierValue); break;
//wave size
case "w": ApplyModifierTo(ref waveSize, modifierValue); break;
}
}
public override string ToString()
{
return $"freq: {frequency}\n" +
$"ampl: {amplitude}\n" +
$"waveSize: {waveSize}" +
$"\n{base.ToString()}";
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f73a8843f32a2af40bd50de80f97ebb9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dd7d9417222ab984ca9326e54c7869e5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,41 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.bh_Bounce)]
class BounceBehavior : BehaviorSine
{
public override void SetDefaultValues(BehaviorDefaultValues data)
{
amplitude = data.defaults.bounceAmplitude;
frequency = data.defaults.bounceFrequency;
waveSize = data.defaults.bounceWaveSize;
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
//Calculates the tween percentage
float BounceTween(float t)
{
const float stillTime = .2f;
const float easeIn = .2f;
const float bounce = 1 - stillTime - easeIn;
if (t <= easeIn)
return Tween.EaseInOut(t / easeIn);
t -= easeIn;
if (t <= bounce)
return 1 - Tween.BounceOut(t / bounce);
return 0;
}
data.vertices.MoveChar(Vector3.up * uniformIntensity * BounceTween((Mathf.Repeat(time.timeSinceStart * frequency - waveSize * charIndex, 1))) * amplitude);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a333962955c4a08498d132d86d9ce19e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.bh_Dangle)]
class DangleBehavior : BehaviorSine
{
float sin;
int targetIndex1;
int targetIndex2;
public override void SetDefaultValues(BehaviorDefaultValues data)
{
amplitude = data.defaults.dangleAmplitude;
frequency = data.defaults.dangleFrequency;
waveSize = data.defaults.dangleWaveSize;
//bottom
if (data.defaults.dangleAnchorBottom)
{
targetIndex1 = 1;
targetIndex2 = 2;
}
else
{
targetIndex1 = 0;
targetIndex2 = 3;
}
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
sin = Mathf.Sin(frequency * time.timeSinceStart + charIndex * waveSize) * amplitude * uniformIntensity;
//moves one side (top or bottom) torwards one direction
data.vertices[targetIndex1] += Vector3.right * sin;
data.vertices[targetIndex2] += Vector3.right * sin;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 01a91f0b9c670b242ae5c34286409cd9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,75 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.bh_Fade)]
class FadeBehavior : BehaviorBase
{
float delay = .3f;
float[] charPCTs;
public override void SetDefaultValues(BehaviorDefaultValues data)
{
delay = data.defaults.fadeDelay;
}
public override void Initialize(int charactersCount)
{
base.Initialize(charactersCount);
charPCTs = new float[charactersCount];
}
public override void SetModifier(string modifierName, string modifierValue)
{
switch (modifierName)
{
//delay
case "d": ApplyModifierTo(ref delay, modifierValue); break;
}
}
Color32 temp;
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
if (data.passedTime <= delay) //not passed enough time yet
return;
charPCTs[charIndex] += time.deltaTime;
if (charPCTs[charIndex] > 1) charPCTs [charIndex] = 1;
//Lerps
if (charPCTs[charIndex] < 1 && charPCTs[charIndex] >= 0)
{
for (var i = 0; i < TextUtilities.verticesPerChar; i++)
{
temp = data.colors[i];
temp.a = 0;
data.colors[i] = Color32.LerpUnclamped(data.colors[i], temp, Tween.EaseInOut(charPCTs[charIndex]));
}
}
else //Keeps them hidden
{
for (var i = 0; i < TextUtilities.verticesPerChar; i++)
{
temp = data.colors[i];
temp.a = 0;
data.colors[i] = temp;
}
}
}
public override string ToString()
{
return $"delay: {delay}\n" +
$"\n{ base.ToString()}";
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1f1dde3a46b6e748885a8d202c32044
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,41 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.bh_Pendulum)]
class PendulumBehavior : BehaviorSine
{
int targetVertex1;
int targetVertex2;
public override void SetDefaultValues(BehaviorDefaultValues data)
{
frequency = data.defaults.pendFrequency;
amplitude = data.defaults.pendAmplitude;
waveSize = data.defaults.pendWaveSize;
if (data.defaults.pendInverted)
{
//anchored at the bottom
targetVertex1 = 0;
targetVertex2 = 3;
}
else
{
//anchored at the top
targetVertex1 = 1;
targetVertex2 = 2;
}
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
data.vertices.RotateChar(
Mathf.Sin(-time.timeSinceStart * frequency + waveSize * charIndex) * amplitude,
(data.vertices[targetVertex1] + data.vertices[targetVertex2]) / 2 //bottom center as their rotation pivot
);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5005fc4984831834d826c8a9e3d42443
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.bh_Rainb)]
class RainbowBehavior : BehaviorBase
{
float hueShiftSpeed = 0.8f;
float hueShiftWaveSize = 0.08f;
public override void SetDefaultValues(BehaviorDefaultValues data)
{
hueShiftSpeed = data.defaults.hueShiftSpeed;
hueShiftWaveSize = data.defaults.hueShiftWaveSize;
}
public override void SetModifier(string modifierName, string modifierValue)
{
switch (modifierName)
{
//frequency
case "f": ApplyModifierTo(ref hueShiftSpeed, modifierValue); break;
//wave size
case "s": ApplyModifierTo(ref hueShiftWaveSize, modifierValue); break;
}
}
Color32 temp;
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
for (byte i = 0; i < TextUtilities.verticesPerChar; i++)
{
//shifts hue
temp = Color.HSVToRGB(Mathf.PingPong(time.timeSinceStart * hueShiftSpeed + charIndex * hueShiftWaveSize, 1), 1, 1);
temp.a = data.colors[i].a; //preserves original alpha
data.colors[i] = temp;
}
}
public override string ToString()
{
return $"hueShiftSpeed: {hueShiftSpeed}\n" +
$"hueShiftWaveSize: {hueShiftWaveSize}" +
$"\n{base.ToString()}";
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5400420051c0a9341be4aece65080148
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.bh_Rot)]
class RotationBehavior : BehaviorBase
{
float angleSpeed = 180;
float angleDiffBetweenChars = 10;
public override void SetDefaultValues(BehaviorDefaultValues data)
{
angleSpeed = data.defaults.angleSpeed;
angleDiffBetweenChars = data.defaults.angleDiffBetweenChars;
}
public override void SetModifier(string modifierName, string modifierValue)
{
switch (modifierName)
{
//frequency
case "f": ApplyModifierTo(ref angleSpeed, modifierValue); break;
//angle diff
case "s": ApplyModifierTo(ref angleDiffBetweenChars, modifierValue); break;
}
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
data.vertices.RotateChar(-time.timeSinceStart * angleSpeed + angleDiffBetweenChars * charIndex);
}
public override string ToString()
{
return $"angleSpeed: {angleSpeed}\n" +
$"angleDiffBetweenChars: {angleDiffBetweenChars}" +
$"\n{base.ToString()}";
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c179eec3e2795104eb216e340b8d8513
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,92 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.bh_Shake)]
class ShakeBehavior : BehaviorBase
{
public float shakeStrength;
public float shakeDelay;
float timePassed;
int randIndex;
public override void SetDefaultValues(BehaviorDefaultValues data)
{
shakeDelay = data.defaults.shakeDelay;
shakeStrength = data.defaults.shakeStrength;
ClampValues();
}
void ClampValues()
{
shakeDelay = Mathf.Clamp(shakeDelay, 0.002f, 500);
}
public override void Initialize(int charactersCount)
{
base.Initialize(charactersCount);
randIndex = Random.Range(0, TextUtilities.fakeRandomsCount);
lastRandomIndex = randIndex;
}
public override void SetModifier(string modifierName, string modifierValue)
{
switch (modifierName)
{
//amplitude
case "a": ApplyModifierTo(ref shakeStrength, modifierValue); break;
//delay
case "d": ApplyModifierTo(ref shakeDelay, modifierValue); break;
}
ClampValues();
}
int lastRandomIndex;
public override void Calculate()
{
timePassed += time.deltaTime;
//Changes the shake direction if enough time passed
if (timePassed >= shakeDelay)
{
timePassed = 0;
randIndex = Random.Range(0, TextUtilities.fakeRandomsCount);
//Avoids repeating the same index twice
if (lastRandomIndex == randIndex)
{
randIndex++;
if (randIndex >= TextUtilities.fakeRandomsCount)
randIndex = 0;
}
lastRandomIndex = randIndex;
}
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
data.vertices.MoveChar
(
TextUtilities.fakeRandoms[
Mathf.RoundToInt((charIndex + randIndex) % (TextUtilities.fakeRandomsCount - 1))
] * shakeStrength * uniformIntensity
);
}
public override string ToString()
{
return $"shake delay: {shakeDelay}\n" +
$"strength: {shakeStrength}" +
$"\n{ base.ToString()}";
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 144066a43449f48469b89b2e2c9ab37d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.bh_Incr)]
sealed class SizeBehavior : BehaviorSine
{
public override void SetDefaultValues(BehaviorDefaultValues data)
{
amplitude = data.defaults.sizeAmplitude * -1 + 1;
frequency = data.defaults.sizeFrequency;
waveSize = data.defaults.sizeWaveSize;
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
data.vertices.LerpUnclamped(
data.vertices.GetMiddlePos(),
(Mathf.Cos(time.timeSinceStart* frequency + charIndex * waveSize) + 1) / 2f * amplitude);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6da0c21b80efda84188912ce510ee1ca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.bh_Slide)]
class SlideBehavior : BehaviorSine
{
float sin;
public override void SetDefaultValues(BehaviorDefaultValues data)
{
amplitude = data.defaults.slideAmplitude;
frequency = data.defaults.slideFrequency;
waveSize = data.defaults.slideWaveSize;
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
sin = Mathf.Sin(frequency * time.timeSinceStart + charIndex * waveSize) * amplitude * uniformIntensity;
//bottom, torwards one direction
data.vertices[0] += Vector3.right * sin;
data.vertices[3] += Vector3.right * sin;
//top, torwards the opposite dir
data.vertices[1] += Vector3.right * -sin;
data.vertices[2] += Vector3.right * -sin;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a740b03d4c3f2d84f9cfc7b3a6f8ea80
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.bh_Swing)]
class SwingBehavior : BehaviorSine
{
public override void SetDefaultValues(BehaviorDefaultValues data)
{
amplitude = data.defaults.swingAmplitude;
frequency = data.defaults.swingFrequency;
waveSize = data.defaults.swingWaveSize;
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
data.vertices.RotateChar(Mathf.Cos(time.timeSinceStart * frequency + charIndex * waveSize) * amplitude);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 63b7822b5552d3748b1acc2a07066822
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.bh_Wave)]
class WaveBehavior : BehaviorSine
{
public override void SetDefaultValues(BehaviorDefaultValues data)
{
amplitude = data.defaults.waveAmplitude;
frequency = data.defaults.waveFrequency;
waveSize = data.defaults.waveWaveSize;
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
data.vertices.MoveChar(Vector3.up * Mathf.Sin(time.timeSinceStart * frequency + charIndex * waveSize) * amplitude * uniformIntensity);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 99de7ce7cdaed2d42b0bd233e69e67db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,59 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: TAnimTags.bh_Wiggle)]
class WiggleBehavior : BehaviorBase
{
float amplitude = 0.15f;
float frequency = 7.67f;
Vector3[] directions;
public override void SetDefaultValues(BehaviorDefaultValues data)
{
amplitude = data.defaults.wiggleAmplitude;
frequency = data.defaults.wiggleFrequency;
}
public override void Initialize(int charactersCount)
{
base.Initialize(charactersCount);
directions = new Vector3[charactersCount];
//Calculates a random direction for each character (which won't change)
for(int i = 0; i < charactersCount; i++)
{
directions[i] = TextUtilities.fakeRandoms[Random.Range(0, TextUtilities.fakeRandomsCount - 1)] * Mathf.Sign(Mathf.Sin(i));
}
}
public override void SetModifier(string modifierName, string modifierValue)
{
switch (modifierName)
{
//amplitude
case "a": ApplyModifierTo(ref amplitude, modifierValue); break;
//frequency
case "f": ApplyModifierTo(ref frequency, modifierValue); break;
}
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
data.vertices.MoveChar(directions[charIndex] * Mathf.Sin(time.timeSinceStart* frequency + charIndex) * amplitude * uniformIntensity);
}
public override string ToString()
{
return $"freq: {frequency}\n" +
$"ampl: {amplitude}" +
$"\n{ base.ToString()}";
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c98870d7ebce9a4fa6c4a16e9701755
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a9e91751a4148a44f9d34bed66d87daf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,149 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[UnityEngine.Scripting.Preserve]
[EffectInfo(tag: "")]
class PresetBehavior : BehaviorBase
{
bool enabled;
//modifiers
float timeSpeed;
float weightMult;
//management
Matrix4x4 matrix;
Vector3 offset;
Quaternion rotationQua;
float uniformEffectTime;
bool hasTransformEffects;
bool isOnOneCharacter;
float weight = 1;
EmissionControl emissionControl;
PresetAppearance.ThreeAxisEffector movement;
PresetAppearance.ThreeAxisEffector rotation;
PresetAppearance.TwoAxisEffector scale;
bool setColor;
Color32 color;
ColorCurve colorCurve;
public override void SetDefaultValues(BehaviorDefaultValues data)
{
weightMult = 1;
timeSpeed = 1;
uniformEffectTime = 0;
weight = 0;
isOnOneCharacter = false;
enabled = false;
void AssignValues(PresetBehaviorValues result)
{
float showDuration = 0;
emissionControl = result.emission;
enabled = PresetAppearance.SetPreset(
false,
result,
ref movement,
ref showDuration,
ref rotation,
ref scale,
ref rotationQua,
ref hasTransformEffects,
ref setColor,
ref colorCurve);
emissionControl.Initialize(showDuration);
}
PresetBehaviorValues values;
//searches for local presets first, which override global presets
if (TAnimBuilder.GetPresetFromArray(effectTag, data.presets, out values))
{
AssignValues(values);
return;
}
//global presets
if (TAnimBuilder.TryGetGlobalPresetBehavior(effectTag, out values))
{
AssignValues(values);
return;
}
}
public override void SetModifier(string modifierName, string modifierValue)
{
switch (modifierName)
{
case "f": //frequency, increases the time speed
ApplyModifierTo(ref timeSpeed, modifierValue);
break;
case "a": //increase the amplitude
ApplyModifierTo(ref weightMult, modifierValue);
break;
}
}
public override void Calculate()
{
if (!isOnOneCharacter)
return;
uniformEffectTime = emissionControl.IncreaseEffectTime(time.deltaTime * timeSpeed);
}
public override void ApplyEffect(ref CharacterData data, int charIndex)
{
if (!enabled)
return;
if (!isOnOneCharacter)
isOnOneCharacter = data.passedTime > 0;
weight = emissionControl.effectWeigth * weightMult;
if (weight == 0) //no effect
return;
if (hasTransformEffects)
{
offset = (data.vertices[0] + data.vertices[2]) / 2f;
//weighted rotation
rotationQua.eulerAngles = rotation.EvaluateEffect(uniformEffectTime, charIndex) * weight;
matrix.SetTRS(
movement.EvaluateEffect(uniformEffectTime, charIndex) * uniformIntensity * weight,
rotationQua,
Vector3.LerpUnclamped(Vector3.one, scale.EvaluateEffect(uniformEffectTime, charIndex), weight));
for (byte i = 0; i < data.vertices.Length; i++)
{
data.vertices[i] -= offset;
data.vertices[i] = matrix.MultiplyPoint3x4(data.vertices[i]);
data.vertices[i] += offset;
}
}
if (setColor)
{
color = colorCurve.GetColor(uniformEffectTime, charIndex);
data.colors.LerpUnclamped(color, Mathf.Clamp(weight, -1, 1));
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f29c16c9fe849e442b4f1bab0c32e28a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[System.Serializable]
internal class PresetBehaviorValues : PresetBaseValues
{
#pragma warning disable 0649 //disabling the error or unity will throw "field is never assigned" [..], because we actually assign the variables from the custom drawers
[SerializeField] public EmissionControl emission;
#pragma warning restore 0649
public override void Initialize(bool isAppearance)
{
base.Initialize(isAppearance);
emission.Initialize(GetMaxDuration());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 22845e594ad09be409f0cac235056a4d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 43d9f8f78afb8b7429ea31773ce88644
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[System.Serializable]
internal class ColorCurve
{
#pragma warning disable 0649 //disabling the error or unity will throw "field is never assigned" [..], because we actually assign the variables from the custom drawers
[SerializeField] public bool enabled;
[SerializeField] protected Gradient gradient;
[SerializeField, Attributes.MinValue(0.1f)] protected float duration;
[SerializeField, Range(0, 100)] protected float charsTimeOffset; //clamping to 100 because it repeates the behavior after it
#pragma warning restore 0649
public float GetDuration()
{
return duration;
}
bool isAppearance;
public void Initialize(bool isAppearance)
{
this.isAppearance = isAppearance;
if (duration < .1f)
{
duration = .1f;
}
}
public Color32 GetColor(float time, int characterIndex)
{
if (isAppearance)
return gradient.Evaluate(Mathf.Clamp01(time / duration));
return gradient.Evaluate(((time / duration) % 1f + characterIndex * (charsTimeOffset / 100f)) % 1f);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a2999cb2db855941a48a04c748af708
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
namespace Febucci.UI.Core
{
interface EffectEvaluator
{
void Initialize(int type);
bool isEnabled { get; }
float Evaluate(float time, int characterIndex);
float GetDuration();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 17f367e912e58ea4d83a71bfcdd83e66
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,96 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[System.Serializable]
internal struct EmissionControl
{
#pragma warning disable 0649 //disables warning 0649, "value declared but never assigned", since Unity actually assigns the variable in the inspector through the [SerializeField] attribute, but the compiler doesn't know this and throws warnings
[SerializeField, Attributes.MinValue(0)] int cycles;
[SerializeField] AnimationCurve attackCurve;
[SerializeField, Attributes.MinValue(0)] float overrideDuration;
[SerializeField] bool continueForever;
[SerializeField] AnimationCurve decayCurve;
#pragma warning restore 0649
[System.NonSerialized] float maxDuration;
[System.NonSerialized] AnimationCurve intensityOverDuration;
[System.NonSerialized] float passedTime;
[System.NonSerialized] float cycleDuration;
[System.NonSerialized] public float effectWeigth;
public void Initialize(float effectsMaxDuration)
{
passedTime = 0;
Keyframe[] totalKeys = new Keyframe[
attackCurve.length + (continueForever ? 0 : decayCurve.length)
];
for (int i = 0; i < attackCurve.length; i++)
{
totalKeys[i] = attackCurve[i];
}
if (!continueForever)
{
if (overrideDuration > 0)
{
effectsMaxDuration = overrideDuration;
}
float attackDuration = attackCurve.CalculateCurveDuration();
for (int i = attackCurve.length; i < totalKeys.Length; i++)
{
totalKeys[i] = decayCurve[i - attackCurve.length];
totalKeys[i].time += effectsMaxDuration + attackDuration;
}
}
intensityOverDuration = new AnimationCurve(totalKeys);
intensityOverDuration.preWrapMode = WrapMode.Loop;
intensityOverDuration.postWrapMode = WrapMode.Loop;
this.cycleDuration = intensityOverDuration.CalculateCurveDuration();
effectWeigth = intensityOverDuration.Evaluate(passedTime); //sets the initial/start weight of the effect, so that effects will be correctly applied on the first frame
maxDuration = cycleDuration * cycles;
}
public float IncreaseEffectTime(float deltaTime)
{
if (deltaTime == 0)
return passedTime;
passedTime += deltaTime;
if (passedTime < 0)
passedTime = 0;
//inside duration
if (passedTime > cycleDuration) //increases cycle
{
if (continueForever)
{
effectWeigth = 1;
return passedTime;
}
}
//outside cycles
if (cycles > 0 && passedTime >= maxDuration)
{
effectWeigth = 0;
return 0;
}
effectWeigth = intensityOverDuration.Evaluate(passedTime);
return passedTime;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1411de3f118750c45a7840e94fb07f7b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,75 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[System.Serializable]
internal class FloatCurve : EffectEvaluator
{
#pragma warning disable 0649 //disabling the error or unity will throw "field is never assigned" [..], because we actually assign the variables from the custom drawers
public bool enabled;
[SerializeField] protected float amplitude;
[SerializeField] protected AnimationCurve curve;
[SerializeField, HideInInspector] protected float defaultReturn;
[SerializeField, Range(0, 100)] protected float charsTimeOffset; //clamping to 100 because it repeates the behavior after it
[System.NonSerialized] float calculatedDuration;
#pragma warning restore 0649
public bool isEnabled => enabled;
public float GetDuration()
{
return calculatedDuration;
}
bool isAppearance;
public void Initialize(int type)
{
calculatedDuration = curve.CalculateCurveDuration();
isAppearance = type >= 3;
switch (type)
{
//mov
default:
case 0: defaultReturn = 0; break;
//scale
case 1: defaultReturn = 1; break;
//rot
case 2: defaultReturn = 0; break;
//app mov
case 3: defaultReturn = 0; break;
//app scale
case 4: defaultReturn = 1; break;
//app rot
case 5: defaultReturn = 0; break;
}
}
public float Evaluate(float time, int characterIndex)
{
if (!enabled)
return defaultReturn;
if (isAppearance)
{
return Mathf.LerpUnclamped(amplitude, defaultReturn, curve.Evaluate(time) * Mathf.Cos(Mathf.Deg2Rad * (characterIndex * charsTimeOffset / 2f)));
}
//behavior
return curve.Evaluate(time + characterIndex * (charsTimeOffset / 100f)) * amplitude;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 54f1446c378588a48a32f2bfc1a623d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
using UnityEngine;
namespace Febucci.UI.Core
{
[System.Serializable]
internal class PresetBaseValues
{
#pragma warning disable 0649 //disabling the error or unity will throw "field is never assigned" [..], because we actually assign the variables from the custom drawers
public string effectTag;
[SerializeField] public FloatCurve movementX;
[SerializeField] public FloatCurve movementY;
[SerializeField] public FloatCurve movementZ;
[SerializeField] public FloatCurve scaleX;
[SerializeField] public FloatCurve scaleY;
[SerializeField] public FloatCurve rotX;
[SerializeField] public FloatCurve rotY;
[SerializeField] public FloatCurve rotZ;
[SerializeField] public ColorCurve color;
#pragma warning restore 0649
public float GetMaxDuration()
{
float GetEffectEvaluatorDuration(EffectEvaluator effect)
{
if (effect.isEnabled)
return effect.GetDuration();
return 0;
}
return Mathf.Max
(
GetEffectEvaluatorDuration(movementX),
GetEffectEvaluatorDuration(movementY),
GetEffectEvaluatorDuration(movementZ),
GetEffectEvaluatorDuration(scaleX),
GetEffectEvaluatorDuration(scaleY),
color.enabled ? color.GetDuration() : 0
);
}
public virtual void Initialize(bool isAppearance)
{
int baseIdentifier = isAppearance ? 3 : 0;
movementX.Initialize(baseIdentifier);
movementY.Initialize(baseIdentifier);
movementZ.Initialize(baseIdentifier);
scaleX.Initialize(baseIdentifier + 1);
scaleY.Initialize(baseIdentifier + 1);
rotX.Initialize(baseIdentifier + 2);
rotY.Initialize(baseIdentifier + 2);
rotZ.Initialize(baseIdentifier + 2);
color.Initialize(isAppearance);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef65b0a3b85a08c468d3b4e67aa0e3e4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 54eccc4b5e3feda488fee2c1faf2922f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More