2026-04-27 BGM 및 이스터에그

This commit is contained in:
2026-04-27 17:47:44 +09:00
parent 88a71e6292
commit 18d3077cc4
840 changed files with 53720 additions and 4 deletions

View File

@@ -0,0 +1,510 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using UnityEngine;
namespace TinyGiantStudio.Text
{
[PreferBinarySerialization]
[CreateAssetMenu(fileName = "New 3D Font", menuName = "Tiny Giant Studio/Modular 3D Text/Font/New Font")]
public class Font : ScriptableObject
{
#region Variable declaration
public List<Character> characters = new List<Character>();
#region Spacing Settings
[Tooltip("Use UpperCase If LowerCase Is Missing")]
public bool useUpperCaseLettersIfLowerCaseIsMissing = true;
[Tooltip("Mono space means all characters are spaced equally.\nIf turned on, individual spacing value from list below is ignored. The information is not removed to avoid accidentally turning it on ruin the font. \nCharacter spacing is used for everything")]
public bool monoSpaceFont;
public float monoSpaceSpacing = 1;
[Tooltip("Word spacing and spacing for unavailable characters")]
public float emptySpaceSpacing = 1;
public float characterSpacing = 1;
public float TabSpace() => emptySpaceSpacing * 3;
#endregion Spacing Settings
[Space]
[Tooltip("Avoid recursive references")]
public List<Font> fallbackFonts = new List<Font>();
[Tooltip("The 3d object with the characters as child object. \nNOT required.")]
public GameObject modelSource = null;
#region Kerning
public bool enableKerning = true;
public float kerningMultiplier = 1f;
//unfortunately dictionary isn't serializable //TODO
public List<KerningPair> kernTable = new List<KerningPair>();
#endregion Kerning
#region Font file data
#region Data from original font file
[Tooltip("An em is a unit of measurement, relative to the size of the font; therefore, in a typeface set at a font-size of 16px, one em is 16px.")]
public float unitPerEM = 1; //multiplied by 8
[Tooltip("Text's character spacing = font's character spacing * text's character spacing")]
public float lineHeight = 0.1469311f;
#endregion Data from original font file
[SerializeField] //It's not serializable though
private TypeFace _typeFace;
public TypeFace TypeFace
{
get
{
if (_typeFace == null)
GetTypeFaceFromBytes();
return _typeFace;
}
set
{
_typeFace = value;
}
}
public byte[] fontBytes;
#endregion Font file data
#region New character creation settings
public int sizeXYInput = 1;
public int sizeZInput = 1;
public float vertexDensityInput = 1;
public float autoSmoothAngleInput = 30;
public float averageYValue = 0;
#endregion New character creation settings
#if UNITY_EDITOR
/// <summary>
/// Editor only. Used by inspector.
/// </summary>
public bool showCharacterDetailsEditor;
/// <summary>
/// Editor only. Used by inspector.
/// </summary>
public string beingSearched;
#endif
#endregion Variable declaration
/// <summary>
/// When this returns null, CharacterGenerator script is used to create it on the fly from ttf data
/// </summary>
/// <param name="c"></param>
/// <param name="checkFallBackFonts"></param>
/// <returns></returns>
public (Mesh, Character) RetrievePrefabAndCharacter(char c, bool checkFallBackFonts = true)
{
//look for character in any form
for (int i = 0; i < characters.Count; i++)
{
if (c == characters[i].character)
{
return (MeshPrefab(i), characters[i]);
}
}
//if no character is found of lower case
if (useUpperCaseLettersIfLowerCaseIsMissing)
{
if (char.IsLower(c))
{
c = char.ToUpper(c);
for (int i = 0; i < characters.Count; i++)
{
if (c == characters[i].character)
{
return (MeshPrefab(i), characters[i]);
}
}
}
}
if (checkFallBackFonts)
{
for (int i = 0; i < fallbackFonts.Count; i++)
{
if (fallbackFonts[i] != null)
{
var missingCharacter = fallbackFonts[i].RetrievePrefabAndCharacter(c, false);
if (missingCharacter.Item1 != null)
{
return (missingCharacter.Item1, missingCharacter.Item2);
}
}
}
}
return (null, null);
}
private Mesh MeshPrefab(int i)
{
if (characters[i].prefab)
{
if (characters[i].prefab.GetComponent<MeshFilter>())
{
if (characters[i].prefab.GetComponent<MeshFilter>().sharedMesh)
{
return characters[i].prefab.GetComponent<MeshFilter>().sharedMesh;
}
}
}
else if (characters[i].meshPrefab)
{
return characters[i].meshPrefab;
}
return null;
}
public float Spacing(char c)
{
if (!monoSpaceFont)
{
for (int i = 0; i < characters.Count; i++)
{
if (c == characters[i].character)
{
return Spacing(characters[i].spacing);
}
}
for (int i = 0; i < fallbackFonts.Count; i++)
{
if (fallbackFonts[i] != null)
{
return fallbackFonts[i].Spacing(c);
}
}
return MonoSpaceSpacing();
}
else //for monospace fonts
{
return MonoSpaceSpacing();
}
}
/// <summary>
/// Spacing with kerning
/// </summary>
/// <param name="previousCharacter"></param>
/// <param name="currentCharacter"></param>
/// <returns></returns>
public float Spacing(char previousCharacter, char currentCharacter)
{
if (!monoSpaceFont)
{
for (int i = 0; i < characters.Count; i++)
{
if (currentCharacter == characters[i].character)
{
return Spacing(characters[i].spacing) + Kerning(previousCharacter, characters[i]);
}
}
for (int i = 0; i < fallbackFonts.Count; i++)
{
if (fallbackFonts[i] != null)
{
return fallbackFonts[i].Spacing(currentCharacter);
}
}
return MonoSpaceSpacing();
}
else //for monospace fonts
{
return MonoSpaceSpacing();
}
}
/// <summary>
/// Used by getcharacterobject.cs
/// </summary>
/// <param name="rawAdvance"></param>
/// <returns></returns>
public float Spacing(float rawAdvance) => ConvertedValue(rawAdvance);
/// <summary>
/// This is the raw value used by unity after taking font EM into consideration
/// </summary>
/// <returns></returns>
public float MonoSpaceSpacing() => ConvertedValue(monoSpaceSpacing);
private float Kerning(char previousChar, Character currentChar)
{
for (int i = 0; i < kernTable.Count; i++)
{
if (kernTable[i].left == previousChar && kernTable[i].right == currentChar.character)
{
return ConvertedValue(kernTable[i].offset * kerningMultiplier);
}
}
return 0;
}
public float Kerning(char previousChar, char currentChar)
{
for (int i = 0; i < kernTable.Count; i++)
{
if (kernTable[i].left == previousChar && kernTable[i].right == currentChar)
{
return ConvertedValue(kernTable[i].offset * kerningMultiplier);
}
}
return 0;
}
public float ConvertedValue(float spacing) => (spacing * characterSpacing) / (Mathf.Clamp(unitPerEM, 0.0001f, 1000000) * 8);
public int KerningReferencesCount(int index)
{
if (index >= characters.Count)
return 0;
int count = 0;
char c = characters[index].character;
for (int i = 0; i < kernTable.Count; i++)
{
if (kernTable[i].left == c || kernTable[i].right == c)
count++;
}
return count;
}
//Font creation:
#region Update Character List start
public void UpdateCharacterList(GameObject prefab)
{
modelSource = prefab;
UpdateCharacterList();
}
public void UpdateCharacterList(bool overwriteOldSet)
{
if (overwriteOldSet)
characters.Clear();
UpdateCharacterList();
}
public void UpdateCharacterList()
{
if (modelSource)
{
foreach (Transform child in modelSource.transform)
{
AddCharacter(child.gameObject);
}
}
else
{
Debug.LogWarning("Model source not found on " + name + " couldn't add any characters");
}
//TabSpace = emptySpaceSpacing * 3;
}
public void AddCharacter(GameObject obj)
{
if (!obj)
return;
ProcessName(obj.name, out char character, out float spacing);
if (CharacterAlreadyExists(character))
return;
Character newChar = new Character();
newChar.character = character;
newChar.spacing = spacing * unitPerEM;
newChar.prefab = obj;
characters.Add(newChar);
}
private bool CharacterAlreadyExists(char character)
{
for (int i = 0; i < characters.Count; i++)
if (characters[i].character == character)
return true;
return false;
}
public void AddCharacter(Mesh mesh)
{
Character newChar = new Character();
if (!mesh)
return;
ProcessName(mesh.name, out char character, out float spacing);
newChar.character = character;
newChar.spacing = spacing;
newChar.meshPrefab = mesh;
characters.Add(newChar);
}
private void ProcessName(string name, out char character, out float spacing)
{
try
{
NewMethod(name, out character, out spacing);
}
catch
{
//Debug.Log("Old method");
OldMethod(name, out character, out spacing);
}
}
private void NewMethod(string name, out char character, out float spacing)
{
string[] s = name.Split(new char[] { '_', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
//Debug.Log(s[0]);
character = Regex.Unescape("\\u" + s[0]).ToCharArray()[0];
//Debug.Log(s[1]);
spacing = GetSpacing(s[1]);
}
/// <summary>
/// Used by the legacy blender font extractor
/// </summary>
/// <param name="name"></param>
/// <param name="character"></param>
/// <param name="spacing"></param>
private void OldMethod(string name, out char character, out float spacing)
{
if (name.Contains("dot"))
{
character = '.';
spacing = (float)Convert.ToDouble(name.Substring(4));
}
else if (name.Contains("forwardSlash"))
{
character = '/';
spacing = GetSpacing(name.Substring(13));
}
else if (name.Contains("quotationMark"))
{
character = '"';
spacing = GetSpacing(name.Substring(14));
}
else if (name.Contains("multiply"))
{
character = '*';
spacing = GetSpacing(name.Substring(9));
}
else if (name.Contains("colon"))
{
character = ':';
spacing = GetSpacing(name.Substring(6));
}
else if (name.Contains("lessThan"))
{
character = '<';
spacing = GetSpacing(name.Substring(9));
}
else if (name.Contains("moreThan"))
{
character = '>';
spacing = GetSpacing(name.Substring(9));
}
else if (name.Contains("questionMark"))
{
character = '?';
spacing = GetSpacing(name.Substring(13));
}
else if (name.Contains("slash"))
{
character = '/';
spacing = GetSpacing(name.Substring(6));
}
else if (name.Contains("backwardSlash"))
{
character = '\\';
spacing = GetSpacing(name.Substring(14));
}
else if (name.Contains("verticalLine"))
{
character = '|';
spacing = GetSpacing(name.Substring(13));
}
else
{
char[] chars = name.ToCharArray();
character = chars[0];
spacing = GetSpacing(name.Substring(2));
}
spacing *= 0.81f;
}
private float GetSpacing(string numberString)
{
if (float.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out var value))
return value;
else
return 1;
}
public void GetMonoSpacingFromAverageCharacterSpacing()
{
float spacing = 0;
for (int i = 0; i < characters.Count; i++)
{
spacing += characters[i].spacing;
}
monoSpaceSpacing = spacing / characters.Count;
if (float.IsNaN(monoSpaceSpacing))
monoSpaceSpacing = 300;
}
#endregion Update Character List start
public void SetFontBytes(byte[] newFontBytes)
{
fontBytes = newFontBytes;
GetTypeFaceFromBytes();
}
public void GetTypeFaceFromBytes()
{
_typeFace = new TypeFace(fontBytes);
}
}
}

View File

@@ -0,0 +1,80 @@
fileFormatVersion: 2
guid: 4ac03f583a899fd43ba438d84cadfb45
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- a: {fileID: 7940836828253788130, guid: f6c071149086dee4986804a8b3d742c6, type: 3}
- b: {fileID: 3845470345730298078, guid: 2ec56972b3f11214b9627e529663f616, type: 3}
- c: {fileID: 805338432506542549, guid: c1b65545b21c369409cc00764d13df4a, type: 3}
- d: {fileID: 444728535302000938, guid: d4236c7e3fb38b448b95ee8928c0e5ed, type: 3}
- e: {instanceID: 0}
- f: {instanceID: 0}
- g: {instanceID: 0}
- h: {instanceID: 0}
- i: {instanceID: 0}
- j: {instanceID: 0}
- k: {instanceID: 0}
- l: {instanceID: 0}
- m: {instanceID: 0}
- n: {instanceID: 0}
- o: {instanceID: 0}
- p: {instanceID: 0}
- q: {instanceID: 0}
- r: {instanceID: 0}
- s: {instanceID: 0}
- t: {instanceID: 0}
- u: {instanceID: 0}
- v: {instanceID: 0}
- w: {instanceID: 0}
- x: {instanceID: 0}
- y: {instanceID: 0}
- z: {instanceID: 0}
- A: {instanceID: 0}
- B: {instanceID: 0}
- C: {instanceID: 0}
- D: {instanceID: 0}
- E: {instanceID: 0}
- F: {instanceID: 0}
- G: {instanceID: 0}
- H: {instanceID: 0}
- I: {instanceID: 0}
- J: {instanceID: 0}
- K: {instanceID: 0}
- L: {instanceID: 0}
- M: {instanceID: 0}
- N: {instanceID: 0}
- O: {instanceID: 0}
- P: {instanceID: 0}
- Q: {instanceID: 0}
- R: {instanceID: 0}
- S: {instanceID: 0}
- T: {instanceID: 0}
- U: {instanceID: 0}
- V: {instanceID: 0}
- W: {instanceID: 0}
- X: {instanceID: 0}
- Y: {instanceID: 0}
- Z: {instanceID: 0}
- zero: {instanceID: 0}
- one: {instanceID: 0}
- two: {instanceID: 0}
- three: {instanceID: 0}
- four: {instanceID: 0}
- five: {instanceID: 0}
- six: {instanceID: 0}
- seven: {instanceID: 0}
- eight: {instanceID: 0}
- nine: {instanceID: 0}
executionOrder: 0
icon: {fileID: 2800000, guid: 71a3aa7acb042be428e37e7c6720ac87, type: 3}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 247241
packageName: Modular 3D Text - In-Game 3D UI System
packageVersion: 4.9.2
assetPath: Assets/Plugins/Tiny Giant Studio/Modular 3D Text/Scripts/Font/Font.cs
uploadId: 877966

View File

@@ -0,0 +1,17 @@
namespace TinyGiantStudio.Text
{
[System.Serializable]
public struct KerningPair
{
public char left;
public float offset;
public char right;
public KerningPair(char left, char right, float offset)
{
this.left = left;
this.offset = offset;
this.right = right;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ea2e2b8a270c0004ab23acd10d2e94fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 71a3aa7acb042be428e37e7c6720ac87, type: 3}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 247241
packageName: Modular 3D Text - In-Game 3D UI System
packageVersion: 4.9.2
assetPath: Assets/Plugins/Tiny Giant Studio/Modular 3D Text/Scripts/Font/KerningPair.cs
uploadId: 877966

View File

@@ -0,0 +1,51 @@
using UnityEditor;
using UnityEngine;
/// <summary>
/// This is used to create a single alphabet
///
/// When to use it:
/// While creating a new font you can use this to fit the font to a standard size so that you can swap fonts ingame and avoid unexpected behaviours
/// The models of your font have different sizes and anchor positions///
///
/// How to use:
/// 1. Create an empty game object
/// 2. Add this to it
/// 3. Add your model as a child object
/// 4. Fit it in the square you see in editor mode
/// 5. Save the game object as a prefab
/// 6. Select your preferred font, assign the PREFAB to the character of your chosing
/// </summary>
namespace TinyGiantStudio.Text
{
[ExecuteInEditMode]
[HelpURL("https://ferdowsur.gitbook.io/modular-3d-text/text/fonts/letter")]
public class Letter : MonoBehaviour
{
public Renderer model; //this is a reference to the actual model with material to swap when typing
#if UNITY_EDITOR
readonly float length = 0.13f, height = 0.13f, depth = .03f; //don't change
readonly float instructionTextHeight = 1;
//void OnDrawGizmosSelected()
void OnDrawGizmos()
{
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = new Color(0, 0, 0, 1f);
Gizmos.DrawWireCube(Vector3.zero, new Vector3(length, height, depth));
// Enable codes below to show a a label with instruction and an arrow. doesnt really do anything
GUIStyle myStyle = new GUIStyle
{
fontSize = 6,
alignment = TextAnchor.UpperCenter
};
Handles.Label(transform.position + new Vector3(0, instructionTextHeight, 0), "fit here, arrow is forward", myStyle);
//Handles.ArrowCap(0, transform.position, transform.rotation * Quaternion.Euler(0, 00, 90), 3);
}
#endif
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 69864063527ab3842af6e5704a538949
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 247241
packageName: Modular 3D Text - In-Game 3D UI System
packageVersion: 4.9.2
assetPath: Assets/Plugins/Tiny Giant Studio/Modular 3D Text/Scripts/Font/Letter.cs
uploadId: 877966

View File

@@ -0,0 +1,41 @@
fileFormatVersion: 2
guid: bd9d7902e80459b45a17b7b77ce1d3b1
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 247241
packageName: Modular 3D Text - In-Game 3D UI System
packageVersion: 4.9.2
assetPath: Assets/Plugins/Tiny Giant Studio/Modular 3D Text/Scripts/Font/M3D Font
Creator - Runtime Only.dll
uploadId: 877966

View File

@@ -0,0 +1,5 @@
//"M3D Font Creator - Editor Only.DLL" is unnecessary and will be removed from future updates.
"M3D Font Creator - Editor Only.DLL" has been removed.
M3D Font Creator - Runtime Only is for both editor and runtime.

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 92e1f374fead04f45a0783a8dcae833e
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 247241
packageName: Modular 3D Text - In-Game 3D UI System
packageVersion: 4.9.2
assetPath: Assets/Plugins/Tiny Giant Studio/Modular 3D Text/Scripts/Font/Readme.txt
uploadId: 877966

View File

@@ -0,0 +1,55 @@
//using System.IO;
using UnityEngine;
namespace TinyGiantStudio.Text
{
/// <summary>
/// This handles importing font files during runtime.
/// Call CreateFontFromTTFFile(byte[] rfontBytes), pass the TTF file as a byte array and it will return a new Font.
/// </summary>
public class RuntimeFontImporter : MonoBehaviour
{
//[SerializeField] string path;
//[ContextMenu("Test")]
//public void Test()
//{
// byte[] fontdata = File.ReadAllBytes(path);
// CreateFontFromTTFFile(fontdata);
//}
/// <summary>
/// Pass the TTF file as a byte array and it will return a new Font.
/// </summary>
/// <param name="fontBytes">Content of the TTF file converted into a byte array.</param>
/// <returns></returns>
public Font CreateFontFromTTFFile(byte[] fontBytes)
{
Font font = ScriptableObject.CreateInstance<Font>();
font.SetFontBytes(fontBytes);
font.GetTypeFaceFromBytes();
font.unitPerEM = font.TypeFace.unitsPerEm;
font.lineHeight = font.TypeFace.lineHeight;
float emptySpaceSpacing = 200;
font.TypeFace.SetGlyphData(' ');
if (font.TypeFace.glyphs.ContainsKey(' '))
{
int index = font.TypeFace.glyphs[' '].glyphIndex;
if (index < font.TypeFace.advanceArray.Length && index >= 0)
{
emptySpaceSpacing = font.TypeFace.advanceArray[index];
}
}
font.emptySpaceSpacing = emptySpaceSpacing;
return font;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 27dfe2c57a577d8408e901b73f7e3dcf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 71a3aa7acb042be428e37e7c6720ac87, type: 3}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 247241
packageName: Modular 3D Text - In-Game 3D UI System
packageVersion: 4.9.2
assetPath: Assets/Plugins/Tiny Giant Studio/Modular 3D Text/Scripts/Font/RuntimeFontImporter.cs
uploadId: 877966