using MiniJSON; using System.Collections; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using TMPro; using UnityEngine; using UnityEngine.TextCore.LowLevel; public class TextMeshFontAssetManager : MonoSingleton<TextMeshFontAssetManager> { private Dictionary<string, TMP_FontAsset> AddFontWithPathList = new Dictionary<string, TMP_FontAsset>(); [DllImport("__Internal")] private static extern string __NT_GetSystemFonts(); public void AddFontAsset(ScriptableObject fontAsset) { if (CheckFontAsset(fontAsset)) return; var def = TMP_Settings.defaultFontAsset; def.fallbackFontAssetTable.Add(fontAsset as TMP_FontAsset); } public void RemoveFontAsset(ScriptableObject fontAsset) { if (!CheckFontAsset(fontAsset)) return; var def = TMP_Settings.defaultFontAsset; def.fallbackFontAssetTable.Remove(fontAsset as TMP_FontAsset); } public bool CheckFontAsset(ScriptableObject fontAsset) { TMP_FontAsset font = fontAsset as TMP_FontAsset; var def = TMP_Settings.defaultFontAsset; return def.fallbackFontAssetTable.Contains(font); } public void AddWithOSFont(List<string> tb) { //这里是获取到设备上的字体路径列表 提取名字把需要的动态添加 Dictionary<string, string> fontPaths = new Dictionary<string, string>(); IEnumerable tempPaths = null; #if UNITY_IPHONE && !UNITY_EDITOR //IOS的Font.GetPathsToOSFonts在2019.4.7之前获取的为空,所以写了个接口获取,接口内容在Unity/IOS笔记本 string jsonData = __NT_GetSystemFonts(); if (!string.IsNullOrEmpty(jsonData)) { tempPaths = Json.Deserialize(jsonData) as List<object>; } #else tempPaths = Font.GetPathsToOSFonts(); #endif if (tempPaths == null) return; foreach (string path in tempPaths) { string key = Path.GetFileNameWithoutExtension(path); if(!fontPaths.ContainsKey(key)) fontPaths.Add(key, path); } for (int i = 0; i < tb.Count; i++) { string fontname = tb[i]; if (fontPaths.ContainsKey(fontname)) { AddFontAssetByFontPath(fontPaths[fontname]); } } } //可以从网上下载字体或获取到本地自带字体 public void AddFontAssetByFontPath(string fontPath) { if (AddFontWithPathList.ContainsKey(fontPath)) return; Font font = new Font(fontPath); TMP_FontAsset tp_font = TMP_FontAsset.CreateFontAsset(font, 20, 2, GlyphRenderMode.SDFAA, 512, 512); AddFontAsset(tp_font); AddFontWithPathList.Add(fontPath, tp_font); } public void RemoveFontAssetByFontPath(string fontPath) { if (!AddFontWithPathList.ContainsKey(fontPath)) return; TMP_FontAsset tp_font = AddFontWithPathList[fontPath]; RemoveFontAsset(tp_font); } public int GetSystemLangeuage() { return (int)Application.systemLanguage; } }
先制作一张包含所有表情的图集,导入Unity,修改图片类型和模式,然后编辑图片,切割为一个一个Sprite表情(可以使用工具自动完成,TexturePacker打包图集和TexturePacker Importer插件将打包的图集转化)
然后右键大的Sprite,创建出Sprite Asset
最后,打开TMP Settings,将创建的Sprite Asset设置为默认的表情
使用:
打开Sprite Asset,可以在SpriteCharacterTable看到一个个的表情和对应的Index
使用的时候输入<sprite=Index>就可以
function M:SetLimitByteLength(length) self.limitLength = length + 1 --不知道为啥 不加1就不正确 self:RemoveOnValidateInput() --self.unity_uitextmeshinput 为Input组件 self.__onValidateInput = handler(self,self.OnValidateInput) self.unity_uitextmeshinput.onValidateInput = self.__onValidateInput end --lua去长度为字节长度 function M:OnValidateInput(text,charIndex,c) if (#(text .. c) > self.limitLength ) then return '\0' --大于的时候返回空字符 此时会输入无效了 else return c end end function M:RemoveOnValidateInput() if self.__onValidateInput ~= nil then self.unity_uitextmeshinput.onValidateInput = nil self.__onValidateInput = nil end end
ContentSizeFitter自适应内容
Horizontal Fit和Vertical Fit如果两个都选择PreferredSize将会优先水平方向的,也就是随着内容的增加,只会修改width属性使用内容。
如果要使用水平方向的,可以固定width宽度,设置Horizontal Fit为Unconstrained、Vertical Fit为PreferredSize,当内容这一个宽度不够放时就会向下扩展修改height了。
背景和文本结构:
背景MsgImage组件(使用九宫格):
文本MsgText组件(程序主要对这个组件进行操作):
结构设置完效果:
程序控制适应方向:
--item.txt_msg 文本组件 --item.fitter_msg 文本的Content Size Fitter组件 --self.MaxPreferredWidth 一行文本允许的最大宽度 --self.PreferredHeight 一行文本的高度 function M:SetItemText(item,content) self:ResetItem(item) --假设水平够用 item.txt_msg:SetText(content) if(item.txt_msg:GetPreferredWidth() > self.MaxPreferredWidth) then --水平宽度超过了允许的最大宽度 item.txt_msg:SetSizeDelta(Vector2(self.MaxPreferredWidth,0)) --设定固定宽度,垂直适应 item.fitter_msg:SetFitVertical() local allTextHight = item.txt_msg:GetPreferredHeight() --获取到文本的总高度,对列表的item高度设置有用 end end function M:ResetItem(item) item.fitter_msg:SetFitHorizontal() item.txt_msg:SetSizeDelta(Vector2(0,self.PreferredHeight)) --固定一行文本高度,水平适应 end
总结:
1.对文本组件挂载ContentSizeFitter,通过PreferredWidth、PreferredHeight来获取宽高
2.如果文本的PreferredWidth超过了最大设置宽度,则使用固定宽度使用垂直适应,如果没有,使用基础高度水平适应
2.背景使用计算的方式设置宽高
using System; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.UI; public class CaptureComponent : MonoBehaviour { private Camera _uguiCamera; private RectTransform rectTransform; private string basePath; private void Awake() { _uguiCamera = GameObject.FindGameObjectWithTag("GuiCamera").GetComponent<Camera>(); rectTransform = this.GetComponent<RectTransform>(); basePath = Application.persistentDataPath + "/share/"; if (!Directory.Exists(basePath)) Directory.CreateDirectory(basePath); } public void CaptureScreenByRect(Action<string> cb,string name="") { if (string.IsNullOrEmpty(name)) { name = basePath + UnityExtends.Calculationmd5(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff"))+".png"; } StartCoroutine(CaptureScreenAsync(name, cb)); } public IEnumerator CaptureScreenAsync(string path, Action<string> cb) { yield return new WaitForEndOfFrame(); try { if (path != "") { Rect rect = rectTransform.rect; Vector3 startWorldPos = rectTransform.TransformPoint(rect.x, rect.y, 0); Vector3 endWorldPos = rectTransform.TransformPoint(rect.x + rect.width, rect.y + rect.height,0); startWorldPos.z = 0; endWorldPos.z = 0; Vector3 startViewPos = _uguiCamera.WorldToViewportPoint(startWorldPos); Vector3 endViewPos = _uguiCamera.WorldToViewportPoint(endWorldPos); rect.x = Screen.width * startViewPos.x; rect.y = Screen.height * startViewPos.y; rect.width = Screen.width * (endViewPos.x - startViewPos.x); rect.height = Screen.height * (endViewPos.y - startViewPos.y); if (rect.x < 0 || rect.y < 0 || (rect.x + rect.width) > Screen.width || (rect.y + rect.height) > Screen.height) { //读取屏幕像素越界,界面不要超过屏幕显示区域 Debug.Log("窗体超过屏幕"); cb?.Invoke(""); } else { //Debug.Log(rect.width + " " + rect.height); Texture2D capture = new Texture2D((int)(rect.width), (int)(rect.height), TextureFormat.RGB24, false); capture.ReadPixels(rect, 0, 0); capture.Apply(); byte[] datas = capture.EncodeToPNG(); File.WriteAllBytes(path, datas); if (cb != null) { Debug.Log(path); cb(path); } } } else cb?.Invoke(""); } catch (Exception e) { Debug.LogError("CaptureScreenByRect:" + e.Message); } } }
Button btn = Images[k].GetComponent<Button>(); if (btn != null) { if (btn.transition == Selectable.Transition.SpriteSwap) { int counter = 0; SpriteState state = btn.spriteState;//通过这个属性获取,是一个结构体 if (state.highlightedSprite == a) { counter++; state.highlightedSprite = b; } if (state.pressedSprite == a) { counter++; state.pressedSprite = b; } if (state.selectedSprite == a) { counter++; state.selectedSprite = b; } if (state.disabledSprite == a) { counter++; state.disabledSprite = b; } if (counter > 0) { btn.spriteState = state; } } }
首先在TextMeshPro的文字显示中标记为<link ID=内容 等下脚本获取到的>展示内容</link>
新建脚本挂载到有这个TextMeshPro组件的物体上
using UnityEngine; using TMPro; using UnityEngine.EventSystems; using System; [RequireComponent(typeof(TextMeshProUGUI))] public class TextMeshProLinkClick :MonoBehaviour, IPointerClickHandler { private TextMeshProUGUI _text; private Camera _uguiCamera; private Action<string> _action; private void Awake() { _text = this.GetComponent<TextMeshProUGUI>(); _uguiCamera = GameObject.FindGameObjectWithTag("GuiCamera").GetComponent<Camera>(); } public void OnPointerClick(PointerEventData eventData) { Vector3 pos = new Vector3(eventData.position.x, eventData.position.y, 0); int linkIndex = TMP_TextUtilities.FindIntersectingLink(_text, pos, _uguiCamera); if (linkIndex > -1) { TMP_LinkInfo linkInfo = _text.textInfo.linkInfo[linkIndex]; if (this._action != null) { //linkInfo.GetLinkID() 获取的就是ID属性的内容 this._action(linkInfo.GetLinkID()); } } } public void SetOnClick(Action<string> callback) { _action = callback; } public void RemoveOnClick() { _action = null; } }
标签内容如果一直到下一个标签且没有镶嵌的不需要关闭标签
1.文字排版
<align="right">Right
<align="center">Center
<align="left">Left
2.颜色
<color="red">Red <color=#005500>Dark Green <#0000FF>Blue <color=#FF000088>Semitransparent Red
<alpha=#FF>FF <alpha=#CC>CC
3.粗体和斜体
The <i>quick brown fox</i> jumps over the <b>lazy dog</b>.
4.字体间距
<cspace=1em>Spacing</cspace>
5.字体
<font="NotoSans" material="NotoSans Outline">a different material?
6.行高
<line-height=50%>Line height at 50%
7.小大写、首字母大写
<lowercase>Alice and Bob watched TV.</lowercase>
<uppercase>Alice and Bob watched TV.</uppercase>
<smallcaps>Alice and Bob watched TV.</smallcaps>
8.标记背景色透明度
<mark=#ffff00aa>can be marked with</mark>
9.不转义标签
<noparse><b></noparse>
10.水平定位
at <pos=75%>75%
at <pos=25%>25%
at <pos=50%>50%
11.字体大小
<size=100%>Echo</size>
12.图片精灵
<sprite name="Default Sprite Asset_4" color=#55FF55FF>
13.删除线和下划线
The <s>quick brown</s> fox jumps over <u>the lazy dog</u>.
14.上下标注
We have 1m<sup>3</sup> of H<sub>2</sub>O.
15.超链接
<link="ID"></link>
Sprite b; Image image; if (b.border.w > 0 || b.border.x > 0 || b.border.y > 0 || b.border.z > 0)//只要有一条边有拖动过,认为有九宫图设置 { image.type = Image.Type.Sliced; } else { image.type = Image.Type.Simple; } image.sprite = b;