  
一、前言
嗨,大家好,我是新发。 我在GitHub 上看到了一个名字叫BullshitGenerator (狗屁不通文章生成器)的项目,地址:https://github.com/menzi11/BullshitGenerator 有接近1.5k 的星星,我下载下来玩了一下,有点意思,不过它是使用Python 和JavaScript 写的,嘛,那我来做一个Unity 版的吧,顺便把文字生成长图的功能也做一下~
二、流程概览
首先,我们要测试一下BullshitGenerator 的功能,看它做了什么; 接着,查看BullshitGenerator 的源码,看它是怎么做的; 最后,我们使用Unity 来实现,并进行拓展,比如生成文字长图。

三、BullshitGenerator项目测试
1、项目下载
BullshitGenerator 项目地址:https://github.com/menzi11/BullshitGenerator
 下载下来后,可以看到它提供了两个版本的实现:Python 和JavaScript , 
2、测试JavaScript版
我们先来玩一下JavaScript 版的,使用浏览器打开index.html ,  在主题输入框中输入文章主题,点击生成按钮,它就可以生成一篇狗屁不通的文章, 
3、测试Python版
现在我们来玩下python 版的,使用python 执行这个自动狗屁不通文章生成器.py ,  输入文章主题,按回车,即可生成一篇狗屁不通的文章, 
四、BullshitGenerator源码分析
1、JavaScript版源码分析
我们使用文本编辑器打开index.html , 
1.1、定义变量(配置表)
它首先定义了几个列表,其实就是配置表,
注:作者使用中文 做为变量名 和函数名 ,一开始看有点不大习惯~
let 论述 = [
"现在,解决主题的问题,是非常非常重要的。 所以, ",
...
]
let 名人名言 = [
"伏尔泰曾经说过,不经巨大的困难,不会有伟大的事业。这不禁令我深思",
...
]
let 后面垫话 = [
"这不禁令我深思。 ",
...
]
let 前面垫话 = [
"曾经说过",
...
]
接着封装一些必要的函数,我来一一讲解一下~
1.2、随便取一句
使用一个Math.random 函数生成一个0~1 的随机数,乘以列表长度,再使用Math.floor 函数向下取整,得到一个列表的随机索引,最后根据索引从列表中取值。
function 随便取一句(列表){
let 坐标 = Math.floor( Math.random() * 列表.length );
return 列表[坐标];
}
1.3、随便取一个数
从最小值和最大值之间随机取一个数,这个是用来做概率随机的,
function 随便取一个数(最小值 = 0,最大值 = 100){
let 数字 = Math.random()*( 最大值 - 最小值 ) + 最小值;
return 数字;
}
1.4、来点名人名言
先从名人名言 列表中随机取一句,然后把这句话中的关键字“曾经说过” 替换为前面垫话 列表的随机一句,再把关键字“这不禁令我深思” 替换为后面垫话 列表的随机一句话,
function 来点名人名言(){
let 名言 = 随便取一句(名人名言)
名言 = 名言.replace("曾经说过", 随便取一句(前面垫话) )
名言 = 名言.replace("这不禁令我深思", 随便取一句(后面垫话) )
return 名言
}
画成图: 
1.5、来点论述
从论述 列表中随机取一句,把关键字"主题" 替换为我们的主题变量 ,注意这里用到了正则表达式,RegExp("主题", "g") 是一个正则表达式,g 表示全局匹配,即把句子中所有的关键字"主题" 都进行匹配,比如我们取到一句论述:"主题的发生,到底需要如何做到,不主题的发生,又会如何产生。 " ,这句论述中是有两个"主题" 关键字的,我们将其替换为我们的主题变量 ,假设我们的主题变量 为:"一天掉多少根头发" ,那么替换后的句子就是:"一天掉多少根头发的发生,到底需要如何做到,不一天掉多少根头发的发生,又会如何产生。 " 。
function 来点论述(){
let 句子 = 随便取一句(论述);
句子 = 句子.replace(RegExp("主题", "g"),主题);
return 句子;
}
画个图: 
1.6、增加段落
检查当前章节的文本的最后是否是一个空格,如果是,把章节的最后两个字符去掉:章节.slice(0,-2) ,为什么要这样做呢?因为配置的论述 和后面垫话 的末尾都是一个标点符号然后留一个空格,比如:"每个人都不得不面对这些问题。 在面对这种问题时, " ,如果在以此句作为章节的最后一句,则需要把,(空格) 替换为句号。 ,章节的开头要空两个空格:" " + 章节 + "。 " 。
function 增加段落(章节){
if(章节[章节.length-1] === " "){
章节 = 章节.slice(0,-2)
}
return " " + 章节 + "。 "
}
画个图: 
1.7、生成文章
获取input 的输入作为主体变量, 文章文字长度小于6000 字的情况下一直循环执行: 5% 的概率且文章字数大于200 字时,生成新的章节; 15% 的概率随机一句名人名言; 80% 的概率随机一句论述; 每个章节都塞入到文章中; 最后显示文章文本到浏览器中。
function 生成文章(){
主题 = $('input').value
let 文章 = []
for(let 空 in 主题){
let 章节 = "";
let 章节长度 = 0;
while( 章节长度 < 6000 ){
let 随机数 = 随便取一个数();
if(随机数 < 5 && 章节.length > 200){
章节 = 增加段落(章节);
文章.push(章节);
章节 = "";
}else if(随机数 < 20){
let 句子 = 来点名人名言();
章节长度 = 章节长度 + 句子.length;
章节 = 章节 + 句子;
}else{
let 句子 = 来点论述();
章节长度 = 章节长度 + 句子.length;
章节 = 章节 + 句子;
}
}
章节 = 增加段落(章节);
文章.push(章节);
}
let 排版 = "<div>" + 文章.join("</div><div>") + "</div>";
$("#论文").innerHTML = 排版;
}
画个图: 
到这里,JavaScript 的源码我们就分析完了,其实整个逻辑很直接,不饶弯子,相信大家看一看就能看懂。下面我们再来看看Python 版的源码吧~
2、Python版源码分析
我们使用文本编辑器打开自动狗屁不通文章生成器.py 。 
2.1、读取配置
可以看到,开头是使用readJSON 模块来读取data.json 配置,存到变量中,
import os, re
import random,readJSON
data = readJSON.读JSON文件("data.json")
名人名言 = data["famous"]
前面垫话 = data["before"]
后面垫话 = data['after']
废话 = data['bosh']
我们可以打开data.json 看看, 
格式是这样的:
{
"title":"主题",
"famous":[
"爱迪生a,天才是百分之一的勤奋加百分之九十九的汗水。b",
"查尔斯·史a,一个人几乎可以在任何他怀有无限热忱的事情上成功。b",
...
],
"bosh":[
"现在,解决x的问题,是非常非常重要的。所以,",
"我们不得不面对一个非常尴尬的事实,那就是,",
...
],
"before":[
"这不禁令我深思。",
"带着这句话,我们还要更加慎重的审视这个问题:",
...
],
"after":[
"曾经说过",
"在不经意间这样说过",
...
],
}
我们再看回readJSON.py 脚本,只有一个函数,就是先判断一下配置文件是不是.json 结尾的,然后open 配置文件,把文件内容read 进来,最后json.loads 把配置的文本(json 格式的字符串)转为python 的字典并return ,
def 读JSON文件(fileName=""):
import json
if fileName!='':
strList = fileName.split(".")
if strList[len(strList)-1].lower() == "json":
with open(fileName,mode='r',encoding="utf-8") as file:
return json.loads(file.read())
画个图: 
2.2、对废话和名人名言进行洗牌
定义一个洗牌方法,然后对废话和名人名言进行洗牌,并返回迭代器,方便后续使用迭代器进行next 操作,
重复度 = 2
def 洗牌遍历(列表):
global 重复度
池 = list(列表) * 重复度
while True:
random.shuffle(池)
for 元素 in 池:
yield 元素
下一句废话 = 洗牌遍历(废话)
下一句名人名言 = 洗牌遍历(名人名言)
注意,上面首先是把列表进行了2 次重复,再进行洗牌。 例:
a = [1,2,3]
b = list(a) * 2
print(b)
另外,上面用到了random.shuffle 函数,它是将序列的所有元素随机排序(即洗牌)。
2.3、来点名人名言
使用next 取出迭代器的下一个项,即下一句名人名言,然后对名言中的关键字"a" 和"b" 做替换: "a" 替换为随机一句前面垫话 ; "b" 替换为随机一句后面垫话 ;
def 来点名人名言():
global 下一句名人名言
xx = next(下一句名人名言)
xx = xx.replace("a", random.choice(前面垫话))
xx = xx.replace("b", random.choice(后面垫话))
return xx
注意,data.json 配置表中的名人名(famous )言格式为: "名人a,名言。b" , 例:
"爱迪生a,天才是百分之一的勤奋加百分之九十九的汗水。b",
"查尔斯·史a,一个人几乎可以在任何他怀有无限热忱的事情上成功。b",
"培根说过,深窥自己的心,而后发觉一切的奇迹在你自己。b",
...
2.4、另起一段
另起一段就是给文章追加句号. ,换行"\r\n" ,新段落开头空四个空格,
def 另起一段():
xx = ". "
xx += "\r\n"
xx += " "
return xx
2.5、main入口:生成文章
提示输入文章主题,当文章字数小于1000 字时循环执行:5% 的概率另起一段,15% 的概率随机一句名人名言,80% 的概率随机一句废话。 最后替换关键字"x" 为主题,输出文章内容。
if __name__ == "__main__":
xx = input("请输入文章主题:")
for x in xx:
tmp = str()
while ( len(tmp) < 1000 ) :
分支 = random.randint(0,100)
if 分支 < 5:
tmp += 另起一段()
elif 分支 < 20 :
tmp += 来点名人名言()
else:
tmp += next(下一句废话)
tmp = tmp.replace("x",xx)
print(tmp)
画个图: 
五、界面设计
好了,现在我们开始动手制作Unity 版本的狗屁不通文章生成器吧~ 我们先使用axure 快速原型设计工具先简单设计一下界面, 
六、UI素材获取
简单的UI 素材资源我是在阿里巴巴矢量图库上找,地址:https://www.iconfont.cn/ 比如搜索按钮,  找一个形状合适的,可以进行调色,我一般是调成白色,  因为Unity 中可以设置Color ,这样我们只需要一个白色按钮就可以在Unity 中创建不同颜色的按钮了。 弄点基础的美术资源, 
七、创建Unity工程
创建一个2D 模板的Unity 工程,工程名我定为UnityBullshitGenerator ,如下:  我想做成竖版的,Game 视图分辨率设置为720*1280 , 
八、制作界面预设:MainPanel.prefab
把上面我们获取的UI 素材导入到Unity 工程中,放在Assets / Textures 目录中,如下:  注意图片类型设置为Sprite (2D and UI) ,  对部分特定的UI 素材使用Sprite Editor 进行九宫格切割,  接着,使用UGUI 制作界面预设:MainPanel.prefab ,保存到Assets /Prefabs 目录中,  预设节点结构如下:  预设显示如下: 
九、配置表:data.json
创建data.json ,内容格式如下,可自行往配置中添加句子(注意有些句子是带可替换的字符的,比如a、b、x )
{
"title":"主题",
"famous":[
"爱迪生a,天才是百分之一的勤奋加百分之九十九的汗水。b",
"查尔斯·史a,一个人几乎可以在任何他怀有无限热忱的事情上成功。b",
...
],
"bosh":[
"现在,解决x的问题,是非常非常重要的。所以,",
"我们不得不面对一个非常尴尬的事实,那就是,",
...
],
"before":[
"这不禁令我深思。",
"带着这句话,我们还要更加慎重的审视这个问题:",
...
],
"after":[
"曾经说过",
"在不经意间这样说过",
...
],
}
将data.json 保存到Unity 工程的Assets/Resources 目录中: 
十、程序代码
程序部分,分为Logic 和View ,Logic 实现逻辑,View 实现界面交互。  BullshitGenerator.cs 和MainPanel.cs 都很好写,难点是Utils.cs :文字如何转Texture2D (文字长图)。不要怕,稍作研究就可以做出来滴,我们先把简单的做了~
1、BullshitGenerator.cs脚本
在Assets / Scripts / Logic 目录中创建BullshitGenerator.cs 脚本, 
BullshitGenerator.cs 脚本要具体做什么呢?首先画一下思维导图: 
1.1、使用LitJson解析配置
我们要解析json 格式的配置表,需要使用Json 库,我推荐使用LitJson 开源库,可以从GitHub 上下载,LitJson 开源项目地址:https://hub.fastgit.org/LitJSON/litjson  我们下载下来后,把src 目录中的LitJson 文件夹整个拷贝到我们Unity 工程中,如下: 
接着我们就可以在代码中使用LitJson 了,使用时引用命名空间
using LitJson;
我们先定义一些容器,用于存放配置表的内容,
private List<string> m_famous = new List<string>();
private List<string> m_bosh = new List<string>();
private List<string> m_after = new List<string>();
private List<string> m_before = new List<string>();
封装一个LoadCfg 方法,实现配置表读取的逻辑,
public void LoadCfg()
{
var jsonText = Resources.Load<TextAsset>("data").text;
var jd = JsonMapper.ToObject(jsonText);
m_famous = JsonMapper.ToObject<List<string>>(jd["famous"].ToJson());
m_bosh = JsonMapper.ToObject<List<string>>(jd["bosh"].ToJson());
m_before = JsonMapper.ToObject<List<string>>(jd["before"].ToJson());
m_after = JsonMapper.ToObject<List<string>>(jd["after"].ToJson());
}
1.2、List洗牌
为了让名人名言和废话的获取具有随机性,我们写一个洗牌函数:
private void Shuffle(ref List<string> list)
{
var r = new System.Random();
for (int i = list.Count - 1; i >= 0; i--)
{
int cardIndex = r.Next(i);
var temp = list[cardIndex];
list[cardIndex] = list[i];
list[i] = temp;
}
}
对名人名言和废话执行一次洗牌:
Shuffle(ref m_famous);
Shuffle(ref m_bosh);
1.3、另起一段
封装一个StartNewLine 函数,实现另起一段的功能(末尾标点符号替换为句号。 并追加一个换行符\n ),
private string StartNewLine(string txt)
{
if (txt.Length > 1)
return txt.Substring(0, txt.Length - 1) + "。\n";
return txt;
}
1.4、获取一句名人名言
封装一个GetFamous 方法,实现获取下一句名人名言的功能,
private string GetFamous()
{
var txt = Next(m_famous, ref m_curFamousIndex);
txt = txt.Replace("a", RandomChoice(m_before));
txt = txt.Replace("b", RandomChoice(m_after));
return txt;
}
private int m_curFamousIndex = 0;
其中Next 方法如下,实现从List 中取下一个索引的值,
private string Next(List<string> list, ref int index)
{
if (index < list.Count - 1)
{
++index;
}
else
{
index = 0;
}
var result = list[index];
return result;
}
RandomChoice 方法如下,实现从List 随机取一个值的功能,
private string RandomChoice(List<string> list)
{
return list[Random.Range(0, list.Count)];
}
1.5、获取一句废话
private int m_curBoshIndex = 0;
var bosh = Next(m_bosh, ref m_curBoshIndex);
1.6、段首空两格
我们需要对文字段落进行格式化,把英文的空格替换为中文的空格符\u3000 ,否则文字排版可能会出问题,如下:
private string FormatSpace(string text)
{
var temp_content = "\u3000\u3000" + text;
temp_content = temp_content.Replace("\n", "\n\u3000\u3000");
temp_content = temp_content.Replace(" ", "\u3000");
return temp_content;
}
1.7、生成文章
封装一个DoGen 函数,实现生成文章的功能,
public string DoGen(string title)
{
string tmp = "";
while (tmp.Length < 1000)
{
var rd = Random.Range(0, 100);
if (rd < 5)
{
tmp = StartNewLine(tmp);
}
else if (rd < 20)
{
tmp += GetFamous();
}
else
{
tmp += Next(m_bosh, ref m_curBoshIndex);
}
}
tmp = StartNewLine(tmp);
tmp = tmp.Replace("x", "<color=#960000ff>" + title + "</color>");
tmp = FormatSpace(tmp);
return tmp;
}
2、MainPanel.cs脚本
在Assets / Scripts / View 目录中创建MainPanel.cs 脚本, 
2.1、UI成员变量
先定义一些必要的UI 成员变量,
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class MainPanel : MonoBehaviour
{
public InputField titleInput;
public Button generateBtn;
public Button saveBtn;
public Text articleText;
}
2.2、初始化
在Awake 中做一下初始化,调用配置加载接口,
private BullshitGenerator m_generator;
private void Awake()
{
m_generator = new BullshitGenerator();
m_generator.LoadCfg();
}
2.3、生成文章按钮的点击逻辑
在Start 函数中,实现生成文章按钮的点击逻辑,调用BullshitGenerator 的DoGen 来生成文章,
void Start()
{
string generateText = "";
generateBtn.onClick.AddListener(() =>
{
generateText = m_generator.DoGen(titleInput.text);
articleText.text = generateText;
});
}
3、测试文章生成
把MainPanel.cs 脚本挂到MainPanel.prefab 预设上,并赋值成员变量。  运行Unity ,测试效果如下,可以正常生成狗屁不通的文章~ 
十一、UGUI的Text如何转成Texture2D长图
现在,我们要将文字保存为长图,也就是把UGUI 的Text 转成Texture2D ,再把Texture2D 存为本地的jpg 文件。 我的思路利用自定义字体生成字体纹理贴图,通过文字的uv 坐标信息获取文字的像素信息,然后绘制到我们动态生成的Texture2D 对象上。
1、制作字体
首先,我们找一个ttf 字体,放到Unity 工程的Assets / Fonts 文件夹中,  将字体的Character 设置为Custom Set ,  在字体统计目录中创建一个character.txt ,  把英文字母、数字、标点符号、常用汉字放到这个character.txt 文件中,如下:  然后把整个文本内容复制,回到Unity 中,粘贴到Font 的Custom Chars 中,点击Apply ,
注:上面存为character.txt 文件不是必要的,但建议你存成文件,方便你查找或修改字符。
 此时字体会生成一个材质和纹理贴图,  字太多了,这样看看不大清,  我们先弄少一点字符,  看它生成的纹理贴图,我们可以看到,我们的字有的是上下颠倒的,有的是顺时针旋转了90 度,(经过我多次测试,发现它只有这两种情况),这个我们后面写文字长图生成的时候需要小心了。 
2、获取字体纹理贴图
var fontTexture = (Texture2D)font.material.mainTexture;
不过字体纹理本身不可读,我们无法对其调用GetPixels 函数,所以我们需要拷贝一份可读的,
var fontTexture = (Texture2D)font.material.mainTexture;
var readableFontTexture = new Texture2D(fontTexture.width,
fontTexture.height,
fontTexture.format,
fontTexture.mipmapCount,
true);
Graphics.CopyTexture(fontTexture, readableFontTexture);
3、根据字符获取对应纹理uv、宽高信息等
char charitem = "哈";
font.GetCharacterInfo(charitem, out CharacterInfo info);
从CharacterInfo 中我们就可以获取到字符的详细信息,包括uv 、宽高等信息, 
4、判断字符纹理是否是上下颠倒和顺时针90度
我们上面可以看到,字符纹理有的是上下颠倒的,我们可以使用uvTopLeft 和uvBottomRight 来判断
if (info.uvTopLeft.y < info.uvBottomRight.y)
{
}
else
{
}
5、获取字符的宽高
字符的实际宽高的获取要小心了,如果是顺时针旋转90 度的字符,则宽和高是对调的,
int charWidth, charHeight;
if (info.uvTopLeft.y < info.uvBottomRight.y)
{
charWidth = info.glyphWidth;
charHeight = info.glyphHeight;
}
else
{
charWidth = info.glyphHeight;
charHeight = info.glyphWidth;
}
6、获取字符的像素信息
我们上面已经得到了可读的字体纹理贴图(Texture2D ),又拿到了字符的uv 坐标、字符宽高信息,那么,我们就可以通过Texture2D 的GetPixels 接口获取到字符的像素信息了,
public Color[] GetPixels(int x, int y, int blockWidth, int blockHeight);
上下颠倒的字符的像素信息的获取:
Color[] charColor = readableFontTexture.GetPixels(
(int)(readableFontTexture.width * info.uvTopLeft.x),
(int)(readableFontTexture.height * info.uvTopLeft.y),
charWidth, charHeight);
顺时针旋转90 度的字符的像素信息的获取:
charColor = readableFontTexture.GetPixels(
(int)(readableFontTexture.width * info.uvBottomRight.x),
(int)(readableFontTexture.height * info.uvBottomRight.y),
charWidth, charHeight);
7、如何给Texture2D写入像素
我们要创建一个长图(背景白色),然后给这个长图挨个字挨个字写入像素,所以,我们先创建一个Texture2D ,并填充背景色,
Color backgroundColor = Color.white;
var textTexture = new Texture2D(textureWidth, textureHeight, TextureFormat.ARGB32, true);
Color[] emptyColor = new Color[textureWidth * textureHeight];
for (int i = 0; i < emptyColor.Length; i++)
{
emptyColor[i] = backgroundColor;
}
textTexture.SetPixels(emptyColor);
接着我们就可以使用SetPixels 接口给这个长图Texture2D 挨个字挨个字写入字符像素了,
foreach (var charitem in text.ToCharArray())
{
textTexture.SetPixel(x, y, color);
}
这里我们需要自己维护一个光标坐标,要控制好光标坐标不能超过Texture2D 外面,还有每个字符的像素尺寸是有大有小的,我们需要计算中心对齐,不然字符就会高高低低(标点符号除外,否则比如逗号就会和文字是中心对齐的,实际上逗号要在文字的脚下)
8、最终Utils的TextToTexture2D接口
在Assets / Scripts / Logic 目录中新建一个Utils.cs 脚本,封装一个TextToTexture2D 接口,如下:
public static Texture2D TextToTexture2D(
Font font,
string text,
int textureWidth, int textureHeight,
int drawOffsetX, int drawOffsetY,
int textGap, int spaceGap, int fontSize,
Color textColor,
Color backgroundColor)
{
var textTexture = new Texture2D(textureWidth, textureHeight, TextureFormat.ARGB32, true);
Color[] emptyColor = new Color[textureWidth * textureHeight];
for (int i = 0; i < emptyColor.Length; i++)
{
emptyColor[i] = backgroundColor;
}
textTexture.SetPixels(emptyColor);
var fontTexture = (Texture2D)font.material.mainTexture;
var readableFontTexture = new Texture2D(fontTexture.width, fontTexture.height, fontTexture.format, fontTexture.mipmapCount, true);
Graphics.CopyTexture(fontTexture, readableFontTexture);
var originalDrawOffsetX = drawOffsetX;
drawOffsetY = textureHeight - drawOffsetY - fontSize;
foreach (var charitem in text.ToCharArray())
{
if ('\u3000' == charitem || ' ' == charitem)
{
drawOffsetX += spaceGap;
continue;
}
if ('\n' == charitem)
{
drawOffsetX = originalDrawOffsetX;
drawOffsetY -= fontSize;
continue;
}
bool isChinese = false;
if (charitem >= 0x4e00 && charitem <= 0x9fbb)
{
isChinese = true;
}
if (drawOffsetX >= textTexture.width - fontSize)
{
drawOffsetX = originalDrawOffsetX;
drawOffsetY -= fontSize;
}
int charWidth, charHeight;
Color[] charColor;
font.GetCharacterInfo(charitem, out CharacterInfo info);
if (info.uvTopLeft.y < info.uvBottomRight.y)
{
charWidth = info.glyphWidth;
charHeight = info.glyphHeight;
charColor = readableFontTexture.GetPixels(
(int)(readableFontTexture.width * info.uvTopLeft.x),
(int)(readableFontTexture.height * info.uvTopLeft.y),
charWidth, charHeight);
for (int j = 0; j < charHeight; j++)
{
for (int i = 0; i < charWidth; i++)
{
if (charColor[j * charWidth + i].a != 0)
{
textTexture.SetPixel(
drawOffsetX + i,
drawOffsetY + charHeight - j + (isChinese ? ((int)((fontSize - charHeight) / 2f)) : 0),
textColor);
}
}
}
drawOffsetX += charWidth + textGap;
}
else
{
charWidth = info.glyphHeight;
charHeight = info.glyphWidth;
charColor = readableFontTexture.GetPixels(
(int)(readableFontTexture.width * info.uvBottomRight.x),
(int)(readableFontTexture.height * info.uvBottomRight.y),
charWidth, charHeight);
for (int j = 0; j < charHeight; j++)
{
for (int i = 0; i < charWidth; i++)
{
if (charColor[j * charWidth + i].a != 0)
{
textTexture.SetPixel(
drawOffsetX + charHeight - j,
drawOffsetY + i + (isChinese ? ((int)((fontSize - charWidth) / 2f)) : 0),
textColor);
}
}
}
drawOffsetX += charHeight + textGap;
}
}
var realTextureHeight = textureHeight - drawOffsetY;
textTexture.Apply();
var finalTexture = new Texture2D(textureWidth, realTextureHeight, TextureFormat.ARGB32, true);
Graphics.CopyTexture(textTexture, 0, 0, 0, drawOffsetY, textureWidth, realTextureHeight, finalTexture, 0, 0, 0, 0);
Object.Destroy(textTexture);
Object.Destroy(readableFontTexture);
return finalTexture;
}
十二、Texture2D长图存为本地jpg文件
我们在Utils 脚本中封装一个SaveTextureToLocal 方法,这个比较简单,就不细讲了~
public static string SaveTextureToLocal(Texture2D texture, string fileName)
{
var bytes = texture.EncodeToJPG();
var savePath = Application.persistentDataPath + "/" + fileName;
#if UNITY_EDITOR
savePath = Application.dataPath + "/" + fileName;
#endif
File.WriteAllBytes(savePath, bytes);
Debug.Log("SaveTextureToLocal: " + savePath);
return savePath;
}
十三、测试文字长图的生成
在MainPanel.cs 脚本中的保存图片按钮的响应中添加Utils 的调用,
public GameObject waitObj;
public GameObject tipsObj;
public Text tipsText;
private Coroutine saveTextureCoroutine;
private void Start()
{
saveBtn.onClick.AddListener(() =>
{
if (null != saveTextureCoroutine)
StopCoroutine(saveTextureCoroutine);
saveTextureCoroutine = StartCoroutine(SaveTexture(generateText));
});
}
private IEnumerator SaveTexture(string generateText)
{
waitObj.SetActive(true);
yield return null;
generateText = generateText.Replace("<color=#960000ff>", "").Replace("</color>", "");
generateText = string.Format("题目:{0}\n{1}\n博主:林新发\n博客:https://blog.csdn.net/linxinfa\n", titleInput.text, generateText);
var texture2D = Utils.TextToTexture2D(articleText.font, generateText,
(int)articleText.rectTransform.rect.width,
(int)articleText.rectTransform.rect.height, 10, 10, 1,
articleText.fontSize, articleText.fontSize, Color.black, Color.white);
var savePath = Utils.SaveTextureToLocal(texture2D, "article.jpg");
tipsObj.SetActive(true);
tipsText.text = "保存成功,路径:\n" + savePath;
waitObj.SetActive(false);
yield return new WaitForSeconds(2);
tipsObj.SetActive(false);
}
把MainPanel.prefab 中显示文章文本的Text 的字体设置为我们自定义的字体,  运行Unity ,生成文章,点击保存图片,
 生成的长图如下(如果觉得不错,记得给我点个赞呀,我可是调试测试了好多次T_T ): 
十四、工程源码
本文工程源码我一上传到CODE CHINA ,感兴趣的同学可自行下载下来学习。 地址:https://codechina.csdn.net/linxinfa/unitybullshitgenerator 注:我使用的Unity 版本为:2021.1.7f1c1 
十五、完毕
好了,就写到这里吧~ 我是林新发:https://blog.csdn.net/linxinfa 原创不易,若转载请注明出处,感谢大家~ 喜欢我的可以点赞、关注、收藏,如果有什么技术上的疑问,欢迎留言或私信~
|