【Unity编辑器扩展】艺术字/自定义图片字体生成工具

艺术字在游戏中很常用,由于普通字体样式过于平淡,制作花里胡哨的文字图片作为游戏字体使用,这就是艺术字。

不依赖第三方工具,仅使用Unity自带的Custom Font + 一张艺术字图集就能实现这个功能,但是为了便于使用,还需要依赖自动化工具,自动化把字符映射到图片纹理坐标,一键生成字体文件。

工具使用效果:

 字符对齐图片:

 艺术字使用效果:

Unity自定义字体参数面板如下:

 其中Character Rects数组是每个字符所在贴图的uv坐标系下的映射Rect:

Index:字符的Ascii码偏移值;字符真实ASCII码 = Ascii Start Offset + Index;这里建议把ASCII码起始值(Ascii Start Offset)设为0,把Index直接设置为字符的ASCII码。

Vert:字符的宽高信息, Y为高度的一半是为了垂直方向居中;

工具功能设计:

0. 首先,制作字符集图片,在Unity中用Sprite Editor进行自动Sprite碎图分割;

1. 拖拽添加字符图集;

2. 设置字符文件或直接在输入框输入字符,两处字符将自动合并去重;

3. 支持设置字体大小;自定义字体无法通过Text组件动态修改字体大小,因此需要为不同字号生成单独的字体文件,但材质和贴图共用,只消耗一个DC;

4.支持字符与图集对照预览;

5. 点击生成,一键创建字体文件和对应材质;

代码实现:

代码实现非常简单,主要就是读取Sprite中的子图Rect信息,然后转换到字体所需的UV坐标系Rect等;

使用Unity 2022新版Sprite编辑器API读取碎图信息:

            var texFact = new SpriteDataProviderFactories();
            texFact.Init();
            var texDataProvider = texFact.GetSpriteEditorDataProviderFromObject(texture2d);
            texDataProvider.InitSpriteEditorDataProvider();
            var spriteRects = texDataProvider.GetSpriteRects();

生成字体文件的Character Rects:

private bool ParseCharsInfo(char[] chars, out CharacterInfo[] charInfoArr, out Texture2D charsTexture)
    {
        charInfoArr = null;
        charsTexture = null;
        if (chars == null || chars.Length < 1)
        {
            return false;
        }
        charsTexture = OwnerEditor.SelectObjectList[0] as Texture2D;
        var texSize = new Vector2Int(charsTexture.width, charsTexture.height);
        var texFact = new SpriteDataProviderFactories();
        texFact.Init();
        var texDataProvider = texFact.GetSpriteEditorDataProviderFromObject(charsTexture);
        texDataProvider.InitSpriteEditorDataProvider();
        var spRects = texDataProvider.GetSpriteRects();
        int count = Mathf.Min(chars.Length, spRects.Length);
        charInfoArr = new CharacterInfo[count];

        for (int i = 0; i < count; i++)
        {
            var spRect = spRects[i].rect;
            var uvMin = spRect.min / texSize;
            var uvMax = spRect.max / texSize;
            float fontHeight = m_FontSize;
            float fontScale = m_FontSize / spRect.height;
            charInfoArr[i] = new CharacterInfo
            {
                index = chars[i],
                uvBottomLeft = uvMin,
                uvBottomRight = new Vector2(uvMax.x, uvMin.y),
                uvTopLeft = new Vector2(uvMin.x, uvMax.y),
                uvTopRight = uvMax,
                minX = 0,
                minY = -(int)(fontHeight * 0.5f),//居中偏移量
                advance = (int)(spRect.width * fontScale),
                glyphWidth = (int)(spRect.width * fontScale),
                glyphHeight = (int)fontHeight,
            };
        }
        return true;
    }

生成字体文件:

string outputDir = EditorUtility.SaveFolderPanel("保存到", Application.dataPath, null);
            if (!string.IsNullOrWhiteSpace(outputDir) && Directory.Exists(outputDir))
            {
                outputDir = Path.Combine("Assets", Path.GetRelativePath(Application.dataPath, outputDir));
                string outputFont = Path.Combine(outputDir, $"{charsTexture.name}_{m_FontSize}.fontsettings");
                Font newFont;
                if (!File.Exists(outputFont))
                {
                    newFont = new Font(charsTexture.name);
                    AssetDatabase.CreateAsset(newFont, outputFont);
                }
                newFont = AssetDatabase.LoadAssetAtPath<Font>(outputFont);
                string outputFontMat = Path.Combine(outputDir, $"{charsTexture.name}.mat");
                if (!File.Exists(outputFontMat))
                {
                    var tempFontMat = new Material(Shader.Find("UI/Default Font"));
                    AssetDatabase.CreateAsset(tempFontMat, outputFontMat);
                }
                var fontMat = AssetDatabase.LoadAssetAtPath<Material>(outputFontMat);
                fontMat.shader = Shader.Find("UI/Default Font");
                fontMat.SetTexture("_MainTex", charsTexture);
                EditorUtility.SetDirty(fontMat);
                AssetDatabase.SaveAssetIfDirty(fontMat);
                newFont.material = fontMat;

                newFont.characterInfo = charInfoArr;
                EditorUtility.SetDirty(newFont);
                AssetDatabase.SaveAssetIfDirty(newFont);
                Selection.activeInstanceID = newFont.GetInstanceID();
            }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/112377.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【AI视野·今日Robot 机器人论文速览 第六十一期】Tue, 24 Oct 2023

AI视野今日CS.Robotics 机器人学论文速览 Tue, 24 Oct 2023 Totally 50 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers Robot Fine-Tuning Made Easy: Pre-Training Rewards and Policies for Autonomous Real-World Reinforcement Learning Autho…

【数据结构】数组和字符串(十三):链式字符串的基本操作(串长统计、查找、复制、插入、删除、串拼接)

文章目录 4.3 字符串4.3.1 字符串的定义与存储4.3.2 字符串的基本操作&#xff08;链式存储&#xff09;1. 结构体2. 初始化3. 判空4. 串尾添加5. 打印6. 串长统计7. 查找8. 复制9. 插入10. 删除11. 串拼接12. 销毁13. 主函数14. 代码整合 4.3 字符串 字符串(String)是由零个或…

2023年CCF中国开源大会“大模型时代的智能化软件工程新范式”分论坛成功举行...

2023年CCF中国开源大会“大模型时代的智能化软件工程新范式”分论坛于10月21日在湖南长沙成功举行。本次论坛聚焦大模型时代的智能化软件新生态以及相应的软件工程新范式&#xff0c;邀请了多位来自学术界和工业界的专家进行分享和交流&#xff0c;共设置了5个主题报告和1个Pan…

K8S删除资源后一直处于Terminating状态无法删除解决方法

原因 使用kubectl delete 删除某命名空间是一直处于Terminating状态无法删除&#xff0c;首先排查了该命名空间下是否还存在deployment pod等资源发现没有后&#xff0c;等了很久还是无法删除后发现是因为该名称空间的“finalizers”字段有值导致 Finalizer&#xff08;终结器…

【OpenCV实现图像梯度,Canny边缘检测】

文章目录 概要图像梯度Canny边缘检测小结 概要 OpenCV中&#xff0c;可以使用各种函数实现图像梯度和Canny边缘检测&#xff0c;这些操作对于图像处理和分析非常重要。 图像梯度通常用于寻找图像中的边缘和轮廓。在OpenCV中&#xff0c;可以使用cv2.Sobel()函数计算图像的梯度…

JVM虚拟机:如何调整堆空间的大小?

对内存的调优 如上所示,从物理角度来说呢,堆内存就是蓝色的区域,从逻辑角度来说,堆内存包含这个红色的部分,调优肯定是条物理的大小了,我们先来看一下物理内存的大小是多少? 如上所示,我们通过maxMemory获取到java虚拟机试图使用的最大内存量,默认为物理内存的1/4,比我…

百度富文本上传图片后样式崩塌

&#x1f525;博客主页&#xff1a; 破浪前进 &#x1f516;系列专栏&#xff1a; Vue、React、PHP ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 问题描述&#xff1a;上传图片后&#xff0c;图片会变得很大&#xff0c;当点击的时候更是会顶开整个的容器的高跟宽 原因&#…

windows版本redis如何设置后踢启动和重启计算机之后自动重启redis

1. 进入redis安装目录 D:\softwarePackage\redis\Redis-x64-3.2.100 2. 打开dos窗口 使用以下命令来启动 Redis 服务器&#xff0c;并使其在后台运行 redis-server --service-start 3. 设置重启自启动 打开服务界面 &#xff08;windowsr 输入 services.msc&#xff09; 找…

第57篇-某钩招聘网站加密参数分析【2023-10-31】

声明:该专栏涉及的所有案例均为学习使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!如有侵权,请私信联系本人删帖! 文章目录 一、前言二、网站分析1.X-S-HEADER参数2.请求参数data3.响应机密值data一、前言 网址: aHR0cHM6Ly93d3cubGFnb3UuY29t…

色彩校正及OpenCV mcc模块介绍

一、术语 1.光&#xff1a;是电磁波&#xff0c;可见光是可被人眼感知的电磁波。可见光大约在400-700nm波段。光子携带的能量与波长成反比&#xff0c;400nm--700nm之间的单色光的颜色从紫色渐变成红色。 2.光谱&#xff1a;除了太阳光源外&#xff0c;LED灯、白炽灯等各种照明…

k8s-调度约束

目录 工作机制 调度过程 指定调度节点 Kubernetes 是通过 List-Watch 的机制进行每个组件的协作&#xff0c;保持数据同步的&#xff0c;每个组件之间的设计实现了解耦。 用户是通过 kubectl 根据配置文件&#xff0c;向 APIServer 发送命令&#xff0c;在 Node 节点上面…

项目管理之项目质量管理MoSCoW(莫斯科)优先级排序法

项目质量管理是项目管理中至关重要的一环&#xff0c;它贯穿于项目的整个生命周期&#xff0c;包括项目启动、规划、执行、监控和控制。为了确保项目工作的质量&#xff0c;我们需要从多个方面入手&#xff0c;以下是一些关于如何保障项目工作质量管理的内容。 项目产品质量检…

兴业银行养老金拉新项目上线啦,地推百搭项目

兴业银行养老金就在 ”聚量推客“ 申请开通 今年最火的银行拉新项目就是养老金的 单价高 数据好 目前开通养老金的银行有 兴业银行养老金拉新 交通银行养老金拉新 工商银行养老金拉新 招商银行养老金拉新 浦发银行养老金拉新 广发银行养老金拉新等。。还有很多都开通了…

206. 反转链表、Leetcode的Python实现

博客主页&#xff1a;&#x1f3c6;看看是李XX还是李歘歘 &#x1f3c6; &#x1f33a;每天分享一些包括但不限于计算机基础、算法等相关的知识点&#x1f33a; &#x1f497;点关注不迷路&#xff0c;总有一些&#x1f4d6;知识点&#x1f4d6;是你想要的&#x1f497; ⛽️今…

leetcode:374. 猜数字大小(二分查找)

一、题目 函数原型&#xff1a;int guessNumber(int n) 二、思路 本题其实就是从 1 - n 中找出所要的答案。利用guess函数来判断数字是否符合答案。 答案小于当前数字&#xff0c;guess函数返回-1 答案等于当前数字&#xff0c;guess函数返回0 答案大于当前数字&#xff0c;gue…

第07章_InnoDB数据存储结构

第07章_InnoDB数据存储结构 1.数据库的存储结构:页 ​ 1.1磁盘与内存交互基本单位:页 ​ 1.2页结构概述 ​ 1.3页的大小 ​ 1.4页的上层结构 2.页的内部结构 ​ 第1部分:File Header(文件头部&#xff09;和File Trailer (文件尾部) 1.数据库的存储结构:页 索引结构给…

SMTP邮件发送图片-如何在github中存储图片并访问

之前写了一篇文章 Go&#xff1a;实现SMTP邮件发送订阅功能&#xff08;包含163邮箱、163企业邮箱、谷歌gmail邮箱&#xff09;&#xff0c;实现了通过邮箱服务来发送邮件&#xff0c;但都是文字内容&#xff0c;要是想实现邮件发送图片&#xff0c;就需要将图片放到公网可访问…

Android开发知识学习——HTTPS

文章目录 定义HTTPS连接HTTPS 连接建立的过程课后题 定义 HTTP Secure / HTTP over SSL / HTTP over TLS SSL&#xff1a;Secure Socket Layer -> TLS Transport Layer Security 定义&#xff1a;在HTTP之下增加的一个安全层&#xff0c;用于保障HTTP的加密传输 本质&…

JavaScript的高级概述

还记得我们刚刚开始的时候给JavaScript的定义吗&#xff1f; JavaScript是一种高级的&#xff0c;面向对象的&#xff0c;多范式变成语言&#xff01; 这种定义JavaScript只是冰山一角&#xff01; JavaScript的高级定义 JavaScript是一种高级的、基于原型的、面向对象、多范…

高效文件整理:按数量划分自动建立文件夹,轻松管理海量文件

在日常生活和工作中&#xff0c;我们经常需要处理大量的文件。然而&#xff0c;如何高效地整理这些文件却是一个棘手的问题。有时候&#xff0c;我们可能需要按照特定的规则来建立文件夹&#xff0c;以便更高效地整理文件。例如&#xff0c;您可以按照日期、时间或者特定的标签…