Unity的UGUI避免行的开头出现符号

这篇具有很好参考价值的文章主要介绍了Unity的UGUI避免行的开头出现符号。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、遇到问题

大家好,我是阿赵。最近在游戏过版署的时候,修改意见里面有一条,游戏内部分文本内容中有标点符号出现在行首的问题。
一般来说,我们编辑文本的时候,是会注意不要把标点符号在换行的时候刚好出现的在行首的,但这个问题,似乎不是策划编辑文本时的问题。这里我会分析问题产生的原因,并附上解决问题的源码。代码写得有点仓促,不确定有没有隐含的问题。如果各位使用的时候发现问题,可以一起交流一下。

二、分析问题出现的原因

下面来看看具体出现的原因:
1、在Canvas Scaler里面选择UI Scale Model 为Scale With Screen Size
Unity的UGUI避免行的开头出现符号,Unity功能与问题解决,unity,游戏引擎,UGUI,自动换行

2、在Text里面设置文本是自动换行的
Unity的UGUI避免行的开头出现符号,Unity功能与问题解决,unity,游戏引擎,UGUI,自动换行

3、在一个分辨率的情况下看,文字并没有出现行首是标点符号的情况。
Unity的UGUI避免行的开头出现符号,Unity功能与问题解决,unity,游戏引擎,UGUI,自动换行

4、换一种分辨率之后,发现某个行首出现了标点符号。
Unity的UGUI避免行的开头出现符号,Unity功能与问题解决,unity,游戏引擎,UGUI,自动换行

通过上面的操作步骤,基本上已经能知道这个问题的出现原因了。
首先,是Scale With Screen Size的缩放方式才会出现问题的,如果使用Constant Pixel Size的缩放方式,是不会有这种问题。
然后,一定是要自动换行才会有这种问题,如果不是自动换行,每行出现的内容不会发生变化
那么我们是不是可以把缩放方式用Constant Pixel Size,并且不用自动换行来解决这个问题呢?
答案明显是否定的。对于一个大型游戏来说,首先适配各种设备分辨率是必须的。如果用Constant Pixel Size,是不会出现换行错乱的问题,但对于不同分辨率的设备来说,固定显示一定分辨率的做法明显是不符合适配的要求的。
然后自动换行的问题也是一样。游戏里面出现的带有文本的UI非常多,同一个UI里面显示的内容,根据游戏进度不一样,也是不一样的,所以很难做到每个界面不自动换行,而逐行Text指定游戏内容的。一般都是设定一个文本框的显示范围,然后自动换行来填充文字。
我个人感觉,这个问题的确是Unity底层自动换行造成的问题,因为Unity的自动换行规则导致的。

说个题外话,如果没兴趣听的可以直接跳到下一节。
这个时候,估计有些朋友已经开始痛骂Unity引擎是垃圾了。我个人认为,问题的确是出在Unity引擎的身世,但垃圾的不是Unity引擎,而是我们国内的技术人员。
为什么这么说呢?这是因为,Unity引擎,是使用英语为母语的人制作出来的,他们考虑问题的出发点,都是以英语为基础的。
比如这个换行的问题,如果输入的不是中文,而是英文。英文单词之间是有空格分隔的,而Unity对于自动换行的规则,是遇到不足以在同一行显示的文本时,会以空格作为切割文本的依据。比如说句末有一个单词连着句号,并且发现这个单词不能完整的显示在同一行,那么Unity会把整个单词加上句号一起换行了。所以,这种问题如果在纯英文的项目里面,基本上是不存在的。
我们可以说Unity没有做好各种语言的本地化适配,但我们更应该意识到,为什么我们要使用一个由英语作为母语并且以英语作为思考问题出发点的引擎呢?为什么我们不直接使用一个由中文作为基础的引擎呢?答案很简单,我们做不出来这种基本的引擎。既然自己做不出来,就不能怪别人垃圾了,只能怪自己没有能力。

言归正传,既然出现问题,我们也只能去解决这个问题。

三、解决问题的

我遇到这个问题的时候,在网上学习了很多前辈的处理方法,总结一下,处理方法基本上是这样的:
1.修改Text的底层,或者继承Text写一个新的Text类
2.监听Text文本变化的方法
3.当文本有变化时,先等当前帧渲染完
4.然后逐行去判断,是否有行首符号,如果有,往前再找一个字符,给他前面强制插入换行符
归纳了别人的解决办法之后,我自己的解决办法稍微不同
1.我并没有修改Text的底层或者继承Text写新的类,而是新建了一个继承MonoBehaviour的类,用于动态挂在有Text的物体上,需要的时候才挂
2.我没有监听Text文本变化的方法,而是自己存储了从外部主动设置到Text的文本,然后通过Update去判断当前的文本是否有变化。这是因为,我不但要关心Text的文本变化,还要关心游戏设备的分辨率变化。当游戏分辨率变化的时候,我不能直接拿Text当前的文本去重新计算,而是要拿最原始的文本去重新计算
3.会出现一种情况,之前没有符号在行首,但由于上面的行插入了新的回车,导致下面的行出现了行首符号的。所以并不能一次性就计算完,需要多次计算直到没有行首符号为止。
4.由于多次计算每次都需要等待当前帧渲染完毕,所以从肉眼可以看出,Text的文本出现闪一下的情况。文章来源地址https://www.toymoban.com/news/detail-744410.html

四、完整代码

using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;

public class TextSymbolWrap : MonoBehaviour
{
    // Start is called before the first frame update
    public Text textCom;
    private string origStr;
    private string replaceStr;
    private string finalReplaceStr;

    /// 标记不换行的空格(换行空格Unicode编码为/u0020,不换行的/u00A0)
	public static readonly string Non_breaking_space = "\u00A0";

    /// 用于匹配标点符号,为了不破坏富文本标签,所以只匹配指定的符号
    private readonly string strPunctuation = @"[,。??!!…]";

    /// 用于存储text组件中的内容
    private System.Text.StringBuilder TempText = null;

    /// 用于存储text生成器中的内容
    private IList<UILineInfo> TextLine;

    private int screenWidth = 0;
    private int screenHeight = 0;
    //在替换后的文本最后面添加一个看不到的字符,以用于辨别当前输入的文本是不是原始文本
    private string endString = " ";

    private bool isReplacing = false;

    void Start()
    {
        
    }
    private void OnEnable()
    {
        CheckTextComponent();
        CheckScreenSizeChange();
        ReplaceTextFun();
    }
    // Update is called once per frame
    void Update()
    {
        if(CheckScreenSizeChange() == true)
        {
            if(textCom!=null&&string.IsNullOrEmpty(origStr)==false)
            {
                if(textCom!=null)
                {
                    textCom.text = origStr;
                }                
                replaceStr = "";
                finalReplaceStr = "";
            }
        }
        CheckReplaceText();
    }

    private bool CheckScreenSizeChange()
    {
        if(Screen.width!=screenWidth||Screen.height!=screenHeight)
        {
            screenWidth = Screen.width;
            screenHeight = Screen.height;
            return true;
        }
        else
        {
            return false;
        }
    }

    private void CheckTextComponent()
    {
        if(textCom!= null)
        {
            return;
        }
        textCom = this.gameObject.GetComponent<Text>();
    }

    private void CheckReplaceText()
    {
        if (textCom == null)
        {
            return;
        }
        if (CheckTextIsChange() == false)
        {
            return;
        }
        ReplaceTextFun();
    }

    private void ReplaceTextFun()
    {
        if(isReplacing == true)
        {
            return;
        }

        replaceStr = "";
        finalReplaceStr = "";
        StartCoroutine("ClearUpPunctuationMode", textCom); 
    }

    private bool CheckTextIsChange()
    {
        if(textCom == null)
        {
            return false;
        }
        string txt = textCom.text;
        if(string.Equals(txt, finalReplaceStr) ==true)
        {
            return false;
        }
        return true;
    }

    IEnumerator ClearUpPunctuationMode(Text _component)
    {
        isReplacing = true;
        //不能立刻就进行计算,要等起码渲染完上一帧才计算,所以延迟了60毫秒
        yield return new WaitForSeconds(0.06f);

        if (string.IsNullOrEmpty(_component.text))
        {
            isReplacing = false;
        }
        else
        {
            //清除上一次添加的换行符号
            //_component.text = _component.text.Replace(" ", string.Empty);
            string tempTxt = _component.text;
            bool isOrigStr = false;
            if (tempTxt[tempTxt.Length - 1].ToString() != endString)
            {
                //如果结尾没有空白字符,就认为是原始的字符串,记录下来用于分辨率改变时再次计算
                origStr = tempTxt;
                isOrigStr = true;
            }
            TextLine = _component.cachedTextGenerator.lines;
            //需要改变的字符序号
            int ChangeIndex = -1;
            TempText = new System.Text.StringBuilder(_component.text);
            for (int i = 1; i < TextLine.Count; i++)
            {
                //首位是否有标点
                UILineInfo lineInfo = TextLine[i];
                int startCharIdx = lineInfo.startCharIdx;
                if(TempText.Length<=startCharIdx)
                {
                    continue;
                }
                bool IsPunctuation = Regex.IsMatch(TempText[startCharIdx].ToString(), strPunctuation);
                //因为将换行空格都改成不换行空格后需要另外判断下如果首字符是不换行空格那么还是需要调整换行字符的下标
                if (TempText[TextLine[i].startCharIdx].ToString() == Non_breaking_space)
                {
                    IsPunctuation = true;
                }

                //没有标点就跳过本次循环
                if (!IsPunctuation)
                {
                    continue;
                }
                else
                {
                    //有标点时保存当前下标
                    ChangeIndex = TextLine[i].startCharIdx;
                    //下面这个循环是为了判断当已经提前一个字符后当前这个的首字符还是标点时做的继续提前字符的处理
                    while (IsPunctuation)
                    {
                        ChangeIndex = ChangeIndex - 1;
                        if (ChangeIndex < 0) break;

                        IsPunctuation = Regex.IsMatch(TempText[ChangeIndex].ToString(), strPunctuation);
                        //因为将换行空格都改成不换行空格后需要另外判断下如果首字符是不换行空格那么还是需要调整换行字符的下标
                        if (TempText[ChangeIndex].ToString() == Non_breaking_space)
                        {
                            IsPunctuation = true;
                        }

                    }
                    if (ChangeIndex < 0) continue;

                    if (TempText[ChangeIndex - 1] != '\n')
                        TempText.Insert(ChangeIndex, "\n");
                }

            }

            replaceStr = TempText.ToString();
            if (string.Equals(tempTxt, replaceStr) == false)
            {
                //如果计算出来的最后结果和text组件当前的字符串不一致,证明有改动,改动后还需要继续判断
                //因为有可能在插入换行后,其他的地方会出现问题
                if (isOrigStr)
                {
                    replaceStr += endString;
                }
                _component.text = replaceStr;

            }
            else
            {
                //计算后的结果和当前text组件的字符串一致,证明当前text组件的字符串已经没有问题
                //记录下来,用于判断当前的字符串是否有改变
                finalReplaceStr = replaceStr;
            }
            isReplacing = false;
        }
    }
}

到了这里,关于Unity的UGUI避免行的开头出现符号的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity中Shader测试常用的UGUI可交互功能的脚本基本使用

    我们在上篇文章简单介绍了一下Shader测试时常用的UGUI功能。 Unity中Shader测试常用的UGUI功能简介 我们在这篇文章中,简单看一下 可交互的UGUI的脚本怎么使用。 public Button _Button; void OnButtonClick() { Debug.Log(“你点击了按钮”); } _Button.onClick.AddListener(OnButtonClick); public Button _Button

    2024年02月04日
    浏览(41)
  • 【UGUI】学会Unity中UGUI中UI元素自适应问题

    彻底学会Unity中UGUI中UI元素自适应问题 官方介绍:设计用于多种分辨率的 UI - Unity 手册 所所谓自适应就是画面元素跟随屏幕分辨率的改变而保持相对位置或者自身像素同步改变! 屏幕分辨率自适应:依靠画布缩放器组件完成 相对位置:依靠锚点位置完成,锚点主要负责保持

    2024年02月04日
    浏览(36)
  • 【学习笔记】Unity基础(七)【uGUI基础、利用render Texture实现小地图功能】

    转载请注明出处:🔗https://blog.csdn.net/weixin_44013533/article/details/130808689 本篇基本是大纲性质,参考价值不大,只有最后一小节“利用render Texture实现小地图功能”花了点时间,可以看看,不过也用到了上面的canvas、UI image等知识、以及input等脚本功能,也算一个小练手吧 倒是

    2024年02月08日
    浏览(47)
  • Unity 问题 之 UGUI 的 Mask 真机打包的遮罩效果失效问题的简单处理

    Unity 问题整理,自己整理的一些游戏开发遇到的问题做简单整理,方便游戏开发。 本节介绍,在使用 cardboard 做 XR 开发中,在使用 Mask  遮罩的时候,编辑器下 Mask 的 遮罩效果正常,但是运行到真机上,Mask 的遮罩效果却失效了,这里简单说明,如果你有更好的方法,欢迎留

    2024年02月15日
    浏览(51)
  • 解决Unity3D打包到PC端,UGUI的格式错乱问题

    Tips:在UI界面开始设计之前,一定先要设置好各项UI元素的参照位置,以及参考的屏幕分辨率比例,不然你精心设计好的UI界面在别人电脑上一看,直接堆在了一起或者根本显示不全,后面再改会很麻烦。 前言:本文主要分两部分,主讲第一部分。并不是第二部分不重要,而

    2024年02月07日
    浏览(51)
  • HTML元素中有中文、英文、符号、数字。第一行没排满就自动换行的解决办法:word-break:break-all的使用

    word-break: break-all 是一个CSS属性,用于控制文本在容器中的换行方式。它的作用是强制在任意字符之间进行换行,即使这样可能会导致单词被分割。 具体来说, word-break 属性有以下几个取值: normal (默认值):默认的换行行为。单词不会被分割,会根据容器的宽度自动换行。

    2024年02月15日
    浏览(44)
  • 【Unity3D小功能】Unity3D中实现点击‘文字’出现‘UI面板’

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 QQ群:398291828 大家好,我是佛系工程师 ☆恬静的小魔龙☆ ,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 宠粉博主又来了,今天有粉丝问我如何实现点击一段文字然后出现的面板在那段文字附近显示: 深入了

    2024年04月13日
    浏览(77)
  • 【Unity 渲染】烘焙渲染出现白色光斑的问题

    Unity场景烘焙后有时会遇到某些物体泛白光,产生白色光斑的问题,例如笔者最近在开发一个三维场景,白光如下:  可以看到在门的背后有白色光斑泛起,内部是这样的: 那么,这个的原因是什么呢? 笔者按以下步骤考虑了这些方面: 一、Lightmap UV是否正确? Lightmap UV如果

    2024年02月02日
    浏览(40)
  • Unity 安装APK后出现两个图标问题

    注意:这个方法合适原本Assets/Plugins/Android路径下没有AndroidManifest清单文件的情况,如果原本这个路径下就存在清单文件,你只需要查看一下清单文件是不是有内容重复就行

    2024年01月22日
    浏览(31)
  • 记录一下Unity使用过程中出现的问题

    1.(2022.3.16) 问题:Unity打开已存在的项目时,一直停留在Hold on... Importing assests界面。 原因及解决方案:Unity Hub中项目设置的默认位置带有中文,将其修改为不含中文的路径即可。 (更新) 重新打开又出现类似问题,一使用VS进行脚本编辑时再次出现加载框,尝试先打开V

    2024年02月08日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包