You can not 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
249 lines
7.0 KiB
//-------------------------------------------------------------------------------------------------------------------------------- |
|
// 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.None; |
|
|
|
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 |
|
} |