You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
7.0 KiB
C#
249 lines
7.0 KiB
C#
//--------------------------------------------------------------------------------------------------------------------------------
|
|
// Cartoon FX
|
|
// (c) 2012-2020 Jean Moreno
|
|
//--------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
namespace CartoonFX
|
|
{
|
|
[RequireComponent(typeof(ParticleSystem))]
|
|
public class CFXR_ParticleText : MonoBehaviour
|
|
{
|
|
#if UNITY_EDITOR
|
|
[Header("Text")]
|
|
public string text;
|
|
public float size = 1f;
|
|
public float letterSpacing = 0.44f;
|
|
|
|
[Header("Colors")]
|
|
public Color backgroundColor = new Color(0, 0, 0, 1);
|
|
public Color color1 = new Color(1, 1, 1, 1);
|
|
public Color color2 = new Color(0, 0, 1, 1);
|
|
|
|
[Header("Delay")]
|
|
public float delay = 0.05f;
|
|
public bool cumulativeDelay = false;
|
|
[Range(0f, 2f)] public float compensateLifetime = 0;
|
|
|
|
[Header("Misc")]
|
|
public float lifetimeMultiplier = 1f;
|
|
[Range(-90f, 90f)] public float rotation = -5f;
|
|
public float sortingFudgeOffset = 0.1f;
|
|
public CFXR_ParticleTextFontAsset font;
|
|
public KeyCode refreshTextKey = KeyCode.R;
|
|
|
|
void OnValidate()
|
|
{
|
|
this.hideFlags = HideFlags.DontSaveInBuild;
|
|
|
|
if (text == null || font == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// parse text to only allow valid characters
|
|
List<char> allowed = new List<char>(font.CharSequence.ToCharArray());
|
|
allowed.Add(' ');
|
|
var chars = text.ToUpperInvariant().ToCharArray();
|
|
string newText = "";
|
|
foreach (var c in chars)
|
|
{
|
|
if (allowed.Contains(c))
|
|
{
|
|
newText += c;
|
|
}
|
|
}
|
|
text = newText;
|
|
|
|
// prevent negative or 0 size
|
|
size = Mathf.Max(0.001f, size);
|
|
}
|
|
|
|
public void GenerateText()
|
|
{
|
|
if (text == null || font == null || !font.IsValid())
|
|
return;
|
|
|
|
// delete all children
|
|
int length = this.transform.childCount;
|
|
int overflow = 0;
|
|
while (this.transform.childCount > 0)
|
|
{
|
|
Object.DestroyImmediate(this.transform.GetChild(0).gameObject);
|
|
overflow++;
|
|
if (overflow > 1000)
|
|
{
|
|
// just in case...
|
|
Debug.LogError("Overflow!");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create one particle per character
|
|
if (!string.IsNullOrEmpty(text))
|
|
{
|
|
// calculate total width offset
|
|
float totalWidth = 0f;
|
|
for (int i = 1; i < text.Length; i++)
|
|
{
|
|
if (char.IsWhiteSpace(text[i]))
|
|
{
|
|
totalWidth += letterSpacing * size;
|
|
}
|
|
else
|
|
{
|
|
int index = font.CharSequence.IndexOf(text[i]);
|
|
var sprite = font.CharSprites[index];
|
|
float charWidth = sprite.rect.width + font.CharKerningOffsets[index].post + font.CharKerningOffsets[index].pre;
|
|
totalWidth += (charWidth * 0.01f + letterSpacing) * size;
|
|
}
|
|
}
|
|
|
|
float offset = totalWidth/2f;
|
|
totalWidth = 0f;
|
|
for (int i = 0; i < text.Length; i++)
|
|
{
|
|
var letter = text[i];
|
|
if (char.IsWhiteSpace(letter))
|
|
{
|
|
totalWidth += letterSpacing * size;
|
|
}
|
|
else
|
|
{
|
|
int index = font.CharSequence.IndexOf(text[i]);
|
|
var sprite = font.CharSprites[index];
|
|
|
|
// calculate char particle size ratio
|
|
var ratio = size * sprite.rect.width/50f;
|
|
|
|
// calculate char position
|
|
totalWidth += font.CharKerningOffsets[index].pre * 0.01f * size;
|
|
var position = (totalWidth-offset)/ratio;
|
|
float charWidth = sprite.rect.width + font.CharKerningOffsets[index].post;
|
|
totalWidth += (charWidth * 0.01f + letterSpacing) * size;
|
|
|
|
// create gameobject with particle system
|
|
var letterObj = new GameObject(letter.ToString(), typeof(ParticleSystem));
|
|
letterObj.transform.SetParent(this.transform);
|
|
letterObj.transform.localPosition = Vector3.zero;
|
|
letterObj.transform.localRotation = Quaternion.identity;
|
|
|
|
var ps = letterObj.GetComponent<ParticleSystem>();
|
|
var sourceParticle = this.GetComponent<ParticleSystem>();
|
|
EditorUtility.CopySerialized(sourceParticle, ps);
|
|
letterObj.name = letter.ToString();
|
|
|
|
// set particle system parameters
|
|
var main = ps.main;
|
|
main.startSizeXMultiplier *= ratio;
|
|
main.startSizeYMultiplier *= ratio;
|
|
main.startSizeZMultiplier *= ratio;
|
|
|
|
ps.textureSheetAnimation.SetSprite(0, sprite);
|
|
|
|
main.startRotation = Mathf.Deg2Rad * rotation;
|
|
main.startColor = backgroundColor;
|
|
|
|
var cd = ps.customData;
|
|
cd.enabled = true;
|
|
cd.SetColor(ParticleSystemCustomData.Custom1, color1);
|
|
cd.SetColor(ParticleSystemCustomData.Custom2, color2);
|
|
|
|
if (cumulativeDelay)
|
|
{
|
|
main.startDelay = delay * i;
|
|
main.startLifetime = Mathf.LerpUnclamped(main.startLifetime.constant, main.startLifetime.constant + (delay * (text.Length-i)), compensateLifetime);
|
|
}
|
|
else
|
|
{
|
|
main.startDelay = delay;
|
|
}
|
|
main.startLifetime = main.startLifetime.constant * lifetimeMultiplier;
|
|
|
|
// particle system renderer parameters
|
|
var psr = ps.GetComponent<ParticleSystemRenderer>();
|
|
var sourceParticleRenderer = this.GetComponent<ParticleSystemRenderer>();
|
|
EditorUtility.CopySerialized(sourceParticleRenderer, psr);
|
|
psr.enabled = true;
|
|
psr.pivot = new Vector3(psr.pivot.x + position, psr.pivot.y, psr.pivot.z);
|
|
psr.sortingFudge += i * sortingFudgeOffset;
|
|
}
|
|
}
|
|
}
|
|
|
|
// restart particle system
|
|
updateFrame = 0;
|
|
if (!editorUpdateSubscribed)
|
|
{
|
|
editorUpdateSubscribed = true;
|
|
EditorApplication.update += RestartParticleSystem;
|
|
}
|
|
}
|
|
|
|
int updateFrame = 0;
|
|
bool editorUpdateSubscribed = false;
|
|
void RestartParticleSystem()
|
|
{
|
|
var particleSystem = this.GetComponent<ParticleSystem>();
|
|
|
|
if (updateFrame == 0)
|
|
{
|
|
particleSystem.Play(true);
|
|
}
|
|
else if (updateFrame == 5)
|
|
{
|
|
particleSystem.time = 0f;
|
|
EditorApplication.update -= RestartParticleSystem;
|
|
editorUpdateSubscribed = false;
|
|
}
|
|
|
|
updateFrame++;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
[CustomEditor(typeof(CFXR_ParticleText))]
|
|
public class ParticleTextEditor : Editor
|
|
{
|
|
public override void OnInspectorGUI()
|
|
{
|
|
var prefab = PrefabUtility.GetPrefabInstanceStatus(target);
|
|
if (prefab != PrefabInstanceStatus.NotAPrefab)
|
|
{
|
|
EditorGUILayout.HelpBox("Cartoon FX Particle Text doesn't work on Prefab Instances, as it needs to destroy/create children GameObjects.\nYou can right-click on the object, and select \"Unpack Prefab Completely\" to make it an independent Game Object.",
|
|
MessageType.Warning);
|
|
return;
|
|
}
|
|
|
|
base.OnInspectorGUI();
|
|
|
|
GUILayout.Space(8);
|
|
GUILayout.BeginHorizontal();
|
|
GUILayout.FlexibleSpace();
|
|
bool refresh = GUILayout.Button(" Refresh Text ", GUILayout.Height(30)) || GUI.changed;
|
|
GUILayout.EndHorizontal();
|
|
|
|
if (((CFXR_ParticleText)target).refreshTextKey != KeyCode.None && Event.current.type == EventType.KeyDown)
|
|
{
|
|
if (Event.current.keyCode == ((CFXR_ParticleText)target).refreshTextKey)
|
|
{
|
|
Event.current.Use();
|
|
refresh = true;
|
|
}
|
|
}
|
|
|
|
if (refresh)
|
|
{
|
|
Undo.RecordObject(this.target, "Generate Particle Text");
|
|
(this.target as CFXR_ParticleText).GenerateText();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
} |