├── Assets ├── ExtendText │ ├── Example │ │ ├── Example.unity │ │ ├── EmojDatabase.asset │ │ ├── Social_Icons │ │ │ ├── icon_github.png │ │ │ ├── icon_reddit.png │ │ │ ├── icon_twitter.png │ │ │ ├── icon_youtube.png │ │ │ ├── icon_facebook.png │ │ │ ├── icon_github.png.meta │ │ │ ├── icon_reddit.png.meta │ │ │ ├── icon_twitter.png.meta │ │ │ ├── icon_youtube.png.meta │ │ │ └── icon_facebook.png.meta │ │ ├── Example.unity.meta │ │ ├── Social_Icons.meta │ │ └── EmojDatabase.asset.meta │ ├── Editor.meta │ ├── Example.meta │ ├── Scripts.meta │ ├── Scripts │ │ ├── EmojImage.cs.meta │ │ ├── ExtendText.cs.meta │ │ ├── EmojDatabase.cs.meta │ │ ├── EmojImage.cs │ │ ├── EmojDatabase.cs │ │ └── ExtendText.cs │ └── Editor │ │ ├── ExtendTextEditor.cs.meta │ │ └── ExtendTextEditor.cs └── ExtendText.meta ├── README.md ├── .gitignore └── LICENSE /Assets/ExtendText/Example/Example.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benloong/UnityGUIExtendText/HEAD/Assets/ExtendText/Example/Example.unity -------------------------------------------------------------------------------- /Assets/ExtendText/Example/EmojDatabase.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benloong/UnityGUIExtendText/HEAD/Assets/ExtendText/Example/EmojDatabase.asset -------------------------------------------------------------------------------- /Assets/ExtendText/Example/Social_Icons/icon_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benloong/UnityGUIExtendText/HEAD/Assets/ExtendText/Example/Social_Icons/icon_github.png -------------------------------------------------------------------------------- /Assets/ExtendText/Example/Social_Icons/icon_reddit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benloong/UnityGUIExtendText/HEAD/Assets/ExtendText/Example/Social_Icons/icon_reddit.png -------------------------------------------------------------------------------- /Assets/ExtendText/Example/Social_Icons/icon_twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benloong/UnityGUIExtendText/HEAD/Assets/ExtendText/Example/Social_Icons/icon_twitter.png -------------------------------------------------------------------------------- /Assets/ExtendText/Example/Social_Icons/icon_youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benloong/UnityGUIExtendText/HEAD/Assets/ExtendText/Example/Social_Icons/icon_youtube.png -------------------------------------------------------------------------------- /Assets/ExtendText/Example/Social_Icons/icon_facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benloong/UnityGUIExtendText/HEAD/Assets/ExtendText/Example/Social_Icons/icon_facebook.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UnityGUIExtendText 2 | uGUI Text 扩展 支持链接 图片 3 | 4 | ![展示](https://user-images.githubusercontent.com/1171605/29955592-5fb3ca32-8f13-11e7-89f2-0658fed9ebd3.gif) 5 | -------------------------------------------------------------------------------- /Assets/ExtendText/Example/Example.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2fc94c84886516244bc5f9af2b531a02 3 | timeCreated: 1504237242 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/ExtendText.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d93b646661daa1543bd20a113de61729 3 | folderAsset: yes 4 | timeCreated: 1504237000 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/ExtendText/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 215f46fa78e39c24185b55f0315d7900 3 | folderAsset: yes 4 | timeCreated: 1504237050 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/ExtendText/Example.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 87a0acb1b5e593f40a59eadcf6ede223 3 | folderAsset: yes 4 | timeCreated: 1504237020 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/ExtendText/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b6e9c2a77c6e28f4b9201526573a5328 3 | folderAsset: yes 4 | timeCreated: 1504237043 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/ExtendText/Example/Social_Icons.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5412159a479054091a33148d3f938a30 3 | folderAsset: yes 4 | timeCreated: 1453884673 5 | licenseType: Store 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/ExtendText/Example/EmojDatabase.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c413ead759b9a864a88a29af4cf6d378 3 | timeCreated: 1504140449 4 | licenseType: Free 5 | NativeFormatImporter: 6 | mainObjectFileID: 11400000 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/ExtendText/Scripts/EmojImage.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 00d1c2192f7c1e8468c853c8d3fe201d 3 | timeCreated: 1504158926 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/ExtendText/Scripts/ExtendText.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b9e167598d305e240818d41482ec3c96 3 | timeCreated: 1504080686 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/ExtendText/Editor/ExtendTextEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d1d621a21347b324cb3518868fdef88a 3 | timeCreated: 1504144997 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/ExtendText/Scripts/EmojDatabase.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3b71c0df4c7d1da47b6c13431ec4221e 3 | timeCreated: 1504140217 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /[Ll]ibrary/ 2 | /[Tt]emp/ 3 | /[Oo]bj/ 4 | /[Bb]uild/ 5 | /[Bb]uilds/ 6 | /Assets/AssetStoreTools* 7 | 8 | # Visual Studio 2015 cache directory 9 | /.vs/ 10 | 11 | # Autogenerated VS/MD/Consulo solution and project files 12 | ExportedObj/ 13 | .consulo/ 14 | *.csproj 15 | *.unityproj 16 | *.sln 17 | *.suo 18 | *.tmp 19 | *.user 20 | *.userprefs 21 | *.pidb 22 | *.booproj 23 | *.svd 24 | *.pdb 25 | 26 | # Unity3D generated meta files 27 | *.pidb.meta 28 | 29 | # Unity3D Generated File On Crash Reports 30 | sysinfo.txt 31 | 32 | # Builds 33 | *.apk 34 | *.unitypackage 35 | -------------------------------------------------------------------------------- /Assets/ExtendText/Scripts/EmojImage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | [RequireComponent(typeof(Image))] 7 | public class EmojImage : MonoBehaviour 8 | { 9 | Emoj emoj; 10 | public Emoj Emoj 11 | { 12 | set 13 | { 14 | emoj = value; 15 | SetImage(); 16 | } 17 | get 18 | { 19 | return emoj; 20 | } 21 | } 22 | 23 | Image image; 24 | Image cachedImage 25 | { 26 | get 27 | { 28 | if (image == null) 29 | { 30 | image = GetComponent(); 31 | } 32 | return image; 33 | } 34 | } 35 | 36 | void SetImage() 37 | { 38 | // 可以做帧动画 39 | cachedImage.sprite = emoj.spriteList[0]; 40 | cachedImage.SetNativeSize(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ben Long 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Assets/ExtendText/Example/Social_Icons/icon_github.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3bc8c223e2b8c41b0902eae695ab3d9f 3 | timeCreated: 1453885441 4 | licenseType: Store 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 0 11 | linearTexture: 0 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -1 30 | maxTextureSize: 256 31 | textureSettings: 32 | filterMode: 2 33 | aniso: 16 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 1 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 8 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /Assets/ExtendText/Example/Social_Icons/icon_reddit.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 156b2513321bd474e89c94f66b86c8b4 3 | timeCreated: 1453884704 4 | licenseType: Store 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 0 11 | linearTexture: 0 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -1 30 | maxTextureSize: 256 31 | textureSettings: 32 | filterMode: 2 33 | aniso: 16 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 1 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 8 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /Assets/ExtendText/Example/Social_Icons/icon_twitter.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7707321257885431b9a47bd8cdc81eb0 3 | timeCreated: 1453884704 4 | licenseType: Store 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 0 11 | linearTexture: 0 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -1 30 | maxTextureSize: 256 31 | textureSettings: 32 | filterMode: 2 33 | aniso: 16 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 1 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 8 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /Assets/ExtendText/Example/Social_Icons/icon_youtube.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 44add2035eca149e887b100431f3d844 3 | timeCreated: 1453884704 4 | licenseType: Store 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 0 11 | linearTexture: 0 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -1 30 | maxTextureSize: 256 31 | textureSettings: 32 | filterMode: 2 33 | aniso: 16 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 1 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 8 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /Assets/ExtendText/Editor/ExtendTextEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | [CustomEditor(typeof(ExtendText), true)] 7 | [CanEditMultipleObjects] 8 | public class ExtendTexttEditor : UnityEditor.UI.TextEditor 9 | { 10 | SerializedProperty emojProp; 11 | SerializedProperty m_Text; 12 | SerializedProperty m_FontData; 13 | SerializedProperty linkColor; 14 | SerializedProperty linkUnderlineColor; 15 | SerializedProperty onHrefClick; 16 | 17 | protected override void OnEnable() 18 | { 19 | base.OnEnable(); 20 | 21 | emojProp = serializedObject.FindProperty("emojDatabase"); 22 | m_Text = serializedObject.FindProperty("m_Text"); 23 | m_FontData = serializedObject.FindProperty("m_FontData"); 24 | linkColor = serializedObject.FindProperty("linkColor"); 25 | linkUnderlineColor = serializedObject.FindProperty("linkUnderlineColor"); 26 | onHrefClick = serializedObject.FindProperty("onHrefClick"); 27 | } 28 | 29 | public override void OnInspectorGUI() 30 | { 31 | serializedObject.Update(); 32 | EditorGUILayout.PropertyField(emojProp); 33 | EditorGUILayout.PropertyField(m_Text); 34 | EditorGUILayout.PropertyField(m_FontData); 35 | AppearanceControlsGUI(); 36 | EditorGUILayout.PropertyField(linkColor); 37 | EditorGUILayout.PropertyField(linkUnderlineColor); 38 | RaycastControlsGUI(); 39 | EditorGUILayout.PropertyField(onHrefClick); 40 | serializedObject.ApplyModifiedProperties(); 41 | } 42 | } -------------------------------------------------------------------------------- /Assets/ExtendText/Scripts/EmojDatabase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [System.Serializable] 6 | public class Emoj 7 | { 8 | public string id; 9 | /// 10 | /// 表情帧,用于实现帧动画 11 | /// 12 | public Sprite[] spriteList; 13 | 14 | public Vector2 Size 15 | { 16 | get 17 | { 18 | if (spriteList.Length == 0) 19 | { 20 | return Vector2.one; 21 | } 22 | 23 | Vector2 sz = spriteList[0].rect.size; 24 | for (int i = 1; i < spriteList.Length; i++) 25 | { 26 | sz = Vector2.Max(sz, spriteList[i].rect.size); 27 | } 28 | return sz; 29 | } 30 | } 31 | 32 | public float size 33 | { 34 | get 35 | { 36 | if (spriteList.Length == 0) 37 | { 38 | return 0; 39 | } 40 | float w = 14; 41 | float h = 14; 42 | for (int i = 0; i < spriteList.Length; i++) 43 | { 44 | w = Mathf.Max(spriteList[i].rect.width, w); 45 | h = Mathf.Max(spriteList[i].rect.height, h); 46 | } 47 | return Mathf.Max(w, h); 48 | } 49 | } 50 | } 51 | 52 | [CreateAssetMenu] 53 | public class EmojDatabase : ScriptableObject 54 | { 55 | public Emoj[] emojs; 56 | 57 | public Emoj GetEmoj(string id) 58 | { 59 | for (int i = 0; i < emojs.Length; i++) 60 | { 61 | if (emojs[i].id == id) 62 | { 63 | return emojs[i]; 64 | } 65 | } 66 | 67 | return null; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Assets/ExtendText/Example/Social_Icons/icon_facebook.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d731e37e7c2f64999801b3c1a8d8e8ef 3 | timeCreated: 1453884704 4 | licenseType: Store 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 4 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 0 11 | sRGBTexture: 1 12 | linearTexture: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 6 25 | cubemapConvolution: 0 26 | seamlessCubemap: 0 27 | textureFormat: -1 28 | maxTextureSize: 256 29 | textureSettings: 30 | filterMode: 2 31 | aniso: 16 32 | mipBias: -1 33 | wrapMode: 1 34 | nPOTScale: 0 35 | lightmap: 0 36 | compressionQuality: 50 37 | spriteMode: 1 38 | spriteExtrude: 1 39 | spriteMeshType: 1 40 | alignment: 0 41 | spritePivot: {x: 0.5, y: 0.5} 42 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 43 | spritePixelsToUnits: 100 44 | alphaUsage: 1 45 | alphaIsTransparency: 1 46 | spriteTessellationDetail: -1 47 | textureType: 8 48 | textureShape: 1 49 | maxTextureSizeSet: 0 50 | compressionQualitySet: 0 51 | textureFormatSet: 0 52 | platformSettings: 53 | - buildTarget: DefaultTexturePlatform 54 | maxTextureSize: 128 55 | textureFormat: -1 56 | textureCompression: 1 57 | compressionQuality: 50 58 | crunchedCompression: 0 59 | allowsAlphaSplitting: 0 60 | overridden: 0 61 | - buildTarget: Standalone 62 | maxTextureSize: 128 63 | textureFormat: -1 64 | textureCompression: 1 65 | compressionQuality: 50 66 | crunchedCompression: 0 67 | allowsAlphaSplitting: 0 68 | overridden: 0 69 | - buildTarget: Android 70 | maxTextureSize: 128 71 | textureFormat: -1 72 | textureCompression: 1 73 | compressionQuality: 50 74 | crunchedCompression: 0 75 | allowsAlphaSplitting: 0 76 | overridden: 0 77 | spriteSheet: 78 | serializedVersion: 2 79 | sprites: [] 80 | outline: [] 81 | spritePackingTag: 82 | userData: 83 | assetBundleName: 84 | assetBundleVariant: 85 | -------------------------------------------------------------------------------- /Assets/ExtendText/Scripts/ExtendText.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.RegularExpressions; 3 | using UnityEngine; 4 | using UnityEngine.Events; 5 | using UnityEngine.EventSystems; 6 | using UnityEngine.UI; 7 | 8 | public class ExtendText : Text , IPointerClickHandler 9 | { 10 | /// 11 | /// 表情格式 <00> 12 | /// 链接格式 <连接名@url> 13 | /// 14 | private static Regex emojRegex = new Regex(@"<(?([0-9]{2}))>|<(?((.+?)\@(.+?)))>"); 15 | 16 | private const string underline = "_"; 17 | 18 | UIVertex[] underlineVerts = new UIVertex[4]; 19 | 20 | /// 21 | /// 表情数据库 22 | /// 23 | [SerializeField] EmojDatabase emojDatabase; 24 | 25 | /// 26 | /// 链接颜色 27 | /// 28 | [SerializeField] Color linkColor = Color.blue; 29 | 30 | /// 31 | /// 链接下划线颜色 32 | /// 33 | [SerializeField] Color linkUnderlineColor = Color.blue; 34 | 35 | [System.Serializable] 36 | public class HrefClickedEvent : UnityEvent 37 | { 38 | } 39 | 40 | [SerializeField] HrefClickedEvent onHrefClick; 41 | 42 | public HrefClickedEvent OnHrefClick 43 | { 44 | get 45 | { 46 | return onHrefClick; 47 | } 48 | set 49 | { 50 | onHrefClick = value; 51 | } 52 | } 53 | 54 | /// 55 | /// 生成后的富文本 包含了自动生成的占位符 等标签 56 | /// 57 | string internalText; 58 | 59 | struct EmojTag 60 | { 61 | /// 62 | /// 开始字符位置 internalText Index 63 | /// 64 | public int charIndex; 65 | 66 | /// 67 | /// 哪个 Emoj 68 | /// 69 | public Emoj emoj; 70 | } 71 | 72 | class HrefTag 73 | { 74 | /// 75 | /// 开始字符位置 internalText Index 76 | /// 77 | public int beginCharIndex; 78 | 79 | /// 80 | /// 结束字符位置 internalText Index 81 | /// 82 | public int endCharIndex; 83 | 84 | /// 85 | /// 描述 显示用 86 | /// 87 | public string desc; 88 | 89 | /// 90 | /// 指向链接 91 | /// 92 | public string link; 93 | 94 | /// 95 | /// 碰撞盒 96 | /// 97 | public List boxes = new List(); 98 | } 99 | 100 | /// 101 | /// Emoj组 102 | /// 103 | RectTransform emojRoot; 104 | 105 | /// 106 | /// emoj 标记表 107 | /// 108 | List emojTags = new List(); 109 | 110 | /// 111 | /// 链接列表 112 | /// 113 | List hrefTags = new List(); 114 | 115 | static readonly UIVertex[] m_TempVerts = new UIVertex[4]; 116 | protected override void OnPopulateMesh(VertexHelper toFill) 117 | { 118 | if (font == null) 119 | return; 120 | 121 | // We don't care if we the font Texture changes while we are doing our Update. 122 | // The end result of cachedTextGenerator will be valid for this instance. 123 | // Otherwise we can get issues like Case 619238. 124 | m_DisableFontTextureRebuiltCallback = true; 125 | 126 | Vector2 extents = rectTransform.rect.size; 127 | 128 | var settings = GetGenerationSettings(extents); 129 | cachedTextGenerator.PopulateWithErrors(internalText, settings, gameObject); 130 | 131 | // Apply the offset to the vertices 132 | IList verts = cachedTextGenerator.verts; 133 | 134 | // 隐藏方块 135 | ClearEmojQuads(verts); 136 | 137 | float unitsPerPixel = 1 / pixelsPerUnit; 138 | //Last 4 verts are always a new line... (\n) 139 | int vertCount = verts.Count - 4; 140 | 141 | Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel; 142 | roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset; 143 | toFill.Clear(); 144 | if (roundingOffset != Vector2.zero) 145 | { 146 | for (int i = 0; i < vertCount; ++i) 147 | { 148 | int tempVertsIndex = i & 3; 149 | m_TempVerts[tempVertsIndex] = verts[i]; 150 | m_TempVerts[tempVertsIndex].position *= unitsPerPixel; 151 | m_TempVerts[tempVertsIndex].position.x += roundingOffset.x; 152 | m_TempVerts[tempVertsIndex].position.y += roundingOffset.y; 153 | if (tempVertsIndex == 3) 154 | toFill.AddUIVertexQuad(m_TempVerts); 155 | } 156 | } 157 | else 158 | { 159 | for (int i = 0; i < vertCount; ++i) 160 | { 161 | int tempVertsIndex = i & 3; 162 | m_TempVerts[tempVertsIndex] = verts[i]; 163 | m_TempVerts[tempVertsIndex].position *= unitsPerPixel; 164 | if (tempVertsIndex == 3) 165 | toFill.AddUIVertexQuad(m_TempVerts); 166 | } 167 | } 168 | 169 | m_DisableFontTextureRebuiltCallback = false; 170 | 171 | // 更新表情位置 172 | PositionEmojs(verts); 173 | 174 | // 生成链接碰撞盒 175 | GenerateHrefHitBox(verts); 176 | 177 | // 生成链接下划线 178 | GenerateUnderline(toFill, settings); 179 | } 180 | 181 | void ClearEmojQuads(IList verts) 182 | { 183 | foreach (var item in emojTags) 184 | { 185 | int vIndex = item.charIndex * 4; 186 | if ((vIndex + 4) > verts.Count) // 被隐藏了,表情也隐藏 187 | break; 188 | for (int i = vIndex; i < vIndex + 4; i++) 189 | { 190 | //清除Quad 191 | UIVertex tempVertex = verts[i]; 192 | tempVertex.uv0 = Vector2.zero; 193 | tempVertex.color = new Color32(0, 0, 0, 0); 194 | verts[i] = tempVertex; 195 | } 196 | } 197 | } 198 | 199 | void PositionEmojs(IList verts) 200 | { 201 | float unitsPerPixel = 1 / pixelsPerUnit; 202 | for (int i = 0; i < emojTags.Count; i++) 203 | { 204 | var child = emojRoot.GetChild(i); 205 | 206 | var childGraphic = child.GetComponent(); 207 | 208 | int vIndex = emojTags[i].charIndex * 4; 209 | if ((vIndex + 4) >= verts.Count) // 最后四个顶点 210 | { 211 | // 直接改alpha会引起Trying to XXX for graphic rebuild while we are already inside a graphic rebuild loop. This is not supported. 212 | // 这里改alpha为0 隐藏 213 | childGraphic.CrossFadeAlpha(0, 0f, true); 214 | continue; 215 | } 216 | 217 | Vector2 min = verts[vIndex].position; 218 | Vector2 max = verts[vIndex].position; 219 | 220 | for (int j = vIndex; j < vIndex + 4; j++) 221 | { 222 | Vector2 pos = verts[j].position; 223 | min.x = Mathf.Min(min.x, pos.x); 224 | min.y = Mathf.Min(min.y, pos.y); 225 | 226 | max.x = Mathf.Max(max.x, pos.x); 227 | max.y = Mathf.Max(max.y, pos.y); 228 | } 229 | 230 | childGraphic.CrossFadeAlpha(1, 0f, true); 231 | child.localPosition = (min + (max - min) / 2 ) * unitsPerPixel; 232 | } 233 | } 234 | 235 | static System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); 236 | 237 | /// 238 | /// 分析处理文本,得到tag和渲染文本 239 | /// 240 | /// 原始文本 241 | /// 渲染文本 242 | void ProcessText(string text) 243 | { 244 | if (string.IsNullOrEmpty(text) == true) 245 | { 246 | internalText = ""; 247 | return; 248 | } 249 | 250 | if (emojDatabase == null) 251 | { 252 | internalText = text; 253 | return; 254 | } 255 | 256 | emojTags.Clear(); 257 | hrefTags.Clear(); 258 | 259 | // clear string buffer 260 | stringBuilder.Length = 0; 261 | 262 | { 263 | int index = 0; 264 | 265 | for (Match match = emojRegex.Match(text); match.Success; match = match.NextMatch()) 266 | { 267 | string val = match.Value; 268 | 269 | stringBuilder.Append(text.Substring(index, match.Index - index)); 270 | index = match.Index + val.Length; 271 | 272 | string href = match.Groups["href"].Value; 273 | string emoj = match.Groups["emoj"].Value; 274 | 275 | if (string.IsNullOrEmpty(emoj) == false) 276 | { 277 | 278 | var emojId = emoj; 279 | 280 | var emojInfo = emojDatabase.GetEmoj(emojId); 281 | 282 | if (emojInfo != null) 283 | { 284 | int charIndex = stringBuilder.Length; 285 | emojTags.Add(new EmojTag() { charIndex = charIndex, emoj = emojInfo }); 286 | Vector2 emojSize = emojInfo.Size; 287 | stringBuilder.AppendFormat("", Mathf.Max(emojSize.x, emojSize.y).ToString()); 288 | } 289 | else 290 | { 291 | stringBuilder.Append(val); 292 | } 293 | } 294 | else if (string.IsNullOrEmpty(href) == false) 295 | { 296 | var content = href; 297 | var pivot = content.IndexOf('@'); 298 | 299 | var desc = content.Substring(0, pivot); 300 | var link = content.Substring(pivot + 1); 301 | 302 | int charIndex = stringBuilder.Length; 303 | 304 | Color32 linkColor = this.linkColor; 305 | string color = string.Format("{0}{1}{2}{3}", linkColor.r.ToString("x2"), linkColor.g.ToString("x2"), linkColor.b.ToString("x2"), linkColor.a.ToString("x2")); 306 | stringBuilder.AppendFormat("{1}",color, desc); 307 | hrefTags.Add(new HrefTag() { beginCharIndex = charIndex, endCharIndex = stringBuilder.Length, desc = desc, link = link }); 308 | } 309 | else 310 | { 311 | stringBuilder.Append(val); 312 | } 313 | } 314 | 315 | stringBuilder.Append(text.Substring(index)); 316 | } 317 | 318 | internalText = stringBuilder.ToString(); 319 | 320 | GenerateEmojs(); 321 | } 322 | 323 | void GenerateEmojs() 324 | { 325 | if (emojRoot == null) 326 | { 327 | emojRoot = rectTransform; 328 | } 329 | 330 | for (int i = 0; i < emojTags.Count; i++) 331 | { 332 | RectTransform rect = null; 333 | if (i < emojRoot.childCount) 334 | { 335 | rect = emojRoot.GetChild(i).GetComponent(); 336 | rect.gameObject.SetActive(true); 337 | } 338 | else 339 | { 340 | rect = new GameObject().AddComponent(); 341 | rect.gameObject.hideFlags = HideFlags.HideAndDontSave; 342 | rect.SetParent(emojRoot, false); 343 | rect.gameObject.AddComponent(); 344 | rect.GetComponent().raycastTarget = false; 345 | } 346 | 347 | rect.GetComponent().Emoj = emojTags[i].emoj; 348 | } 349 | 350 | for (int i = emojTags.Count; i < emojRoot.childCount; i++) 351 | { 352 | emojRoot.GetChild(i).gameObject.SetActive(false); 353 | } 354 | } 355 | 356 | void GenerateHrefHitBox(IList verts) 357 | { 358 | foreach (var tag in hrefTags) 359 | { 360 | int begin = tag.beginCharIndex * 4; 361 | int end = tag.endCharIndex * 4; 362 | 363 | if (verts.Count < begin) 364 | { 365 | // 后面的顶点被隐藏了 366 | break; 367 | } 368 | 369 | var boundsList = tag.boxes; 370 | boundsList.Clear(); 371 | 372 | Bounds bounds = new Bounds(verts[begin].position, Vector3.zero); 373 | 374 | for (int i = begin; i < end && i < verts.Count - 4; i++) // 最后4个顶点是换行符 375 | { 376 | var pos = verts[i].position; 377 | 378 | if (pos.x < bounds.min.x) // 换行 379 | { 380 | if (bounds.size.x > 0 && bounds.size.y > 0) // 检测不是个空盒子 381 | { 382 | tag.boxes.Add(new Rect(bounds.min, bounds.size)); 383 | } 384 | bounds = new Bounds(pos, Vector3.zero); 385 | } 386 | else 387 | { 388 | bounds.Encapsulate(pos); 389 | } 390 | } 391 | 392 | if (bounds.size.x > 0 && bounds.size.y > 0) // 检测不是个空盒子 393 | { 394 | tag.boxes.Add(new Rect(bounds.min, bounds.size)); 395 | } 396 | } 397 | } 398 | 399 | void GenerateUnderline(VertexHelper toFill, TextGenerationSettings settings) 400 | { 401 | UIVertex[] underlineVerts = GetUnderlineVerts(settings); 402 | 403 | foreach (var tag in hrefTags) 404 | { 405 | foreach (var box in tag.boxes) 406 | { 407 | if (box.width > 0 && box.height > 0) 408 | { 409 | FillUnderline(box, toFill, underlineVerts); 410 | } 411 | } 412 | } 413 | } 414 | 415 | void FillUnderline(Rect box, VertexHelper toFill, UIVertex[] underlineVerts) 416 | { 417 | /* 面片结构 418 | * p0-------p1 419 | * | | 420 | * | | 421 | * p3-------p2 422 | * */ 423 | 424 | box.position -= new Vector2(0, 1); //向下偏移一个单位 425 | 426 | Vector3 p0 = underlineVerts[0].position; 427 | Vector3 p1 = underlineVerts[1].position; 428 | Vector3 p2 = underlineVerts[2].position; 429 | 430 | float height = p1.y - p2.y; 431 | float width = p1.x - p0.x; 432 | 433 | Vector2 uv0 = underlineVerts[0].uv0; 434 | Vector2 uv1 = underlineVerts[1].uv0; 435 | Vector2 uv2 = underlineVerts[2].uv0; 436 | Vector2 uv3 = underlineVerts[3].uv0; 437 | 438 | //顶部中心uv 439 | Vector2 topCenterUv = uv0 + (uv1 - uv0) * 0.5f; 440 | //底部中心uv 441 | Vector2 bottomCenterUv = uv3 + (uv2 - uv3) * 0.5f; 442 | 443 | m_TempVerts[0] = underlineVerts[0]; 444 | m_TempVerts[1] = underlineVerts[1]; 445 | m_TempVerts[2] = underlineVerts[2]; 446 | m_TempVerts[3] = underlineVerts[3]; 447 | 448 | m_TempVerts[0].color = linkUnderlineColor; 449 | m_TempVerts[1].color = linkUnderlineColor; 450 | m_TempVerts[2].color = linkUnderlineColor; 451 | m_TempVerts[3].color = linkUnderlineColor; 452 | 453 | float xMin = box.xMin; 454 | float yMin = box.yMin; 455 | 456 | float xMax = box.xMax; 457 | float yMax = box.yMin + height; // 高度 458 | 459 | float unitsPerPixel = 1 / pixelsPerUnit; 460 | { 461 | m_TempVerts[0].position = new Vector3(xMin, yMax) * unitsPerPixel; 462 | m_TempVerts[0].uv0 = uv0; 463 | 464 | m_TempVerts[1].position = new Vector3(xMin + width * 0.5f, yMax) * unitsPerPixel; 465 | m_TempVerts[1].uv0 = topCenterUv; 466 | 467 | m_TempVerts[2].position = new Vector3(xMin + width * 0.5f, yMin) * unitsPerPixel; 468 | m_TempVerts[2].uv0 = bottomCenterUv; 469 | 470 | m_TempVerts[3].position = new Vector3(xMin, yMin) * unitsPerPixel; 471 | m_TempVerts[3].uv0 = uv3; 472 | 473 | toFill.AddUIVertexQuad(m_TempVerts); 474 | } 475 | { 476 | m_TempVerts[0].position = new Vector3(xMin + width * 0.5f, yMax) * unitsPerPixel; 477 | m_TempVerts[0].uv0 = topCenterUv; 478 | 479 | m_TempVerts[1].position = new Vector3(xMax - width * 0.5f, yMax) * unitsPerPixel; 480 | m_TempVerts[1].uv0 = topCenterUv; 481 | 482 | m_TempVerts[2].position = new Vector3(xMax - width * 0.5f, yMin) * unitsPerPixel; 483 | m_TempVerts[2].uv0 = bottomCenterUv; 484 | 485 | m_TempVerts[3].position = new Vector3(xMin + width * 0.5f, yMin) * unitsPerPixel; 486 | m_TempVerts[3].uv0 = bottomCenterUv; 487 | 488 | toFill.AddUIVertexQuad(m_TempVerts); 489 | } 490 | 491 | { 492 | m_TempVerts[0].position = new Vector3(xMax - width * 0.5f, yMax) * unitsPerPixel; 493 | m_TempVerts[0].uv0 = topCenterUv; 494 | 495 | m_TempVerts[1].position = new Vector3(xMax, yMax) * unitsPerPixel; 496 | m_TempVerts[1].uv0 = uv1; 497 | 498 | m_TempVerts[2].position = new Vector3(xMax, yMin) * unitsPerPixel; 499 | m_TempVerts[2].uv0 = uv2; 500 | 501 | m_TempVerts[3].position = new Vector3(xMax - width * 0.5f, yMin) * unitsPerPixel; 502 | m_TempVerts[3].uv0 = bottomCenterUv; 503 | 504 | toFill.AddUIVertexQuad(m_TempVerts); 505 | } 506 | } 507 | 508 | UIVertex[] GetUnderlineVerts(TextGenerationSettings settings) 509 | { 510 | cachedTextGenerator.Populate(underline, settings); 511 | var verts = cachedTextGenerator.verts; 512 | 513 | for (int i = 0; i < verts.Count && i < 4; i++) 514 | { 515 | underlineVerts[i] = verts[i]; 516 | } 517 | 518 | return underlineVerts; 519 | } 520 | 521 | public override float preferredWidth 522 | { 523 | get 524 | { 525 | var settings = GetGenerationSettings(Vector2.zero); 526 | return cachedTextGeneratorForLayout.GetPreferredWidth(internalText, settings) / pixelsPerUnit; 527 | } 528 | } 529 | 530 | public override float preferredHeight 531 | { 532 | get 533 | { 534 | var settings = GetGenerationSettings(new Vector2(rectTransform.rect.size.x, 0.0f)); 535 | return cachedTextGeneratorForLayout.GetPreferredHeight(internalText, settings) / pixelsPerUnit; 536 | } 537 | } 538 | 539 | public override void SetVerticesDirty() 540 | { 541 | ProcessText(text); 542 | base.SetVerticesDirty(); 543 | } 544 | 545 | protected override void OnDisable() 546 | { 547 | SetEnableChild(false); 548 | base.OnDisable(); 549 | } 550 | 551 | protected override void OnEnable() 552 | { 553 | ForceRichTextAndAlignByGeometry(); 554 | base.OnEnable(); 555 | SetEnableChild(true); 556 | } 557 | 558 | void SetEnableChild(bool enable) 559 | { 560 | for (int i = 0; emojRoot != null && i < emojRoot.childCount; i++) 561 | { 562 | emojRoot.GetChild(i).GetComponent().enabled = enable; 563 | } 564 | } 565 | 566 | protected void ForceRichTextAndAlignByGeometry() 567 | { 568 | supportRichText = true; 569 | alignByGeometry = true; 570 | } 571 | 572 | public void OnPointerClick(PointerEventData eventData) 573 | { 574 | Vector2 lp; 575 | RectTransformUtility.ScreenPointToLocalPointInRectangle( 576 | rectTransform, eventData.position, eventData.pressEventCamera, out lp); 577 | 578 | foreach (var hrefInfo in hrefTags) 579 | { 580 | var boxes = hrefInfo.boxes; 581 | for (var i = 0; i < boxes.Count; ++i) 582 | { 583 | if (boxes[i].Contains(lp)) 584 | { 585 | OnHrefClick.Invoke(hrefInfo.link); 586 | return; 587 | } 588 | } 589 | } 590 | } 591 | 592 | #if UNITY_EDITOR 593 | protected override void OnValidate() 594 | { 595 | ForceRichTextAndAlignByGeometry(); 596 | base.OnValidate(); 597 | } 598 | #endif 599 | } 600 | --------------------------------------------------------------------------------