Vamos fazer um layout misto de gráficos e texto no Unity~

Faz muito tempo que não atualizo, e até minha conta foi perdida, demorei muito para acertar a senha.
Ando muito ocupada ultimamente, e quase fiz hora extra no último domingo. Felizmente...
não vamos falar besteira, vamos começar! ! !

Absurdo

Em primeiro lugar, este título é um absurdo e pode ser ignorado.

Gráficos e texto mistos, esta é a função mais comum em um projeto. Isso mesmo, nosso projeto também tem esse requisito, felizmente não sei como fazer! Caso contrário, eu não estaria escrevendo este artigo.
Não só não vai, como até acho que isso é problemático e quero atrasá-lo o máximo possível. No entanto, eles arranjaram muito tempo para mim e não posso mais fazer isso.

Então comecei a Baidu para ver as coisas escritas por outras pessoas.
Veja a primeira, faça um atlas das imagens que você precisa e cole-as com o Mesh. Bem, as limitações são muito grandes e está além da minha compreensão. Não sei se pode funcionar normalmente quando eu o cito e tenho que imprimir um atlas. Não entendo o desempenho e Não vou mudar se houver problemas... Então
o segundo é fazer um arquivo Asset, adicionar as fotos necessárias, nomeá-lo você mesmo, definir o tamanho... Pessoalmente, acho que não é tão bom quanto o primeiro, mas é usado para fazer efeitos gif. Mas a maioria dos projetos não precisa de coisas tão problemáticas, é melhor apenas obter um plug-in Skeleton e aplicar o terceiro método.
O último é o terceiro método, que é modificar o método OnPopulateMesh de Text.No processo de obtenção de informações de vértice, altere a string da imagem para um espaço com uma largura adequada por meio de regularização e registre os dados relevantes da imagem. Em seguida, monte várias imagens no objeto e atribua as informações da imagem a ele.

A terceira forma parece boa, principalmente porque o código que ele deu está completo e a lógica é fácil de ver. No entanto, depois de me atrapalhar com isso, encontrei muitos problemas, principalmente as posições erradas. Só posso mudá-lo silenciosamente. Depois de muito tempo, finalmente parece quase o mesmo.

Dois meses se passaram de forma tão tranquila, e uma lista veio [Em diferentes resoluções, a posição de gráficos e textos misturados está errada] Era
muito desconfortável, então tive que reler pouco a pouco e comecei a desmoronar para ver cada item. Informações do vértice. Finalmente resolveu muitos problemas, por exemplo, o número de espaços ocupados pela imagem está errado, o cálculo do vértice no centro da posição da imagem está errado, a quebra de linha e o espaço original não são calculados, etc. ... Com certeza, as pessoas não podem ser preguiçosas. Não havia nada como antes
. Eu vi completamente através disso, e agora sou forçado a resolver a lógica completamente. -_-

Dessa forma, pensei que finalmente havia acabado, mas ontem fiz outra lista [No celular, ocasionalmente há apenas fotos, mas nenhum texto]
Isso me deixou muito angustiado e comecei várias investigações e, finalmente, provavelmente sei, Deve ser o problema do OnPopulateMesh, mas como devo mudar isso...
Até a noite, eu estava pensando sobre esse problema antes de ir para a cama e de repente pensei: posso substituir o método set de text e substituir o text por o final ao inserir o resultado. Dessa forma, OnPopulateMesh é acionado sob a lógica subjacente, o que não deve ser um problema.

Então, hoje vim para a empresa, fui ao git para encontrar o código-fonte do UGUI2019 e observei a lógica do texto. Observe o método OnPopulateMesh substituído e execute diretamente o processamento regular em m_Text no método set de text. Eu tentei, mas funciona.
Quando estava feliz, encontrei um novo problema. Por ser processado no método set, o valor de m_Text será completamente substituído. Isso significa que, se estiver diretamente no pré-fabricado, o valor original será completamente perdido.

emmmmm, eu tive que mudá-lo de volta. Parece que só podemos ver o que há de errado com o método OnPopulateMesh. Continuarei a examinar a maneira como OnPopulateMesh é escrito no código-fonte. Descobri que existem muitas diferenças em relação aos métodos que encontrei na Internet.
Entendo, porque o método que copiei é de 2017 e o projeto atual é de 2019, então a lógica subjacente é diferente. Este deve ser o problema.
Mas novas preocupações voltam, porque o git é de 2019.1.05 e meu projeto é de 2019.4, não posso garantir que o código seja válido. E se 2018 ou 2020 ou mesmo 2025 for usado posteriormente?
Tive que reorganizar a lógica e descobri que o texto foi chamado no método OnPopulateMesh, o que significa que o núcleo da substituição do OnPopulateMesh é obter e modificar o texto para ficar correto. Então posso fazer cálculos e modificações no método get de texto.
Experimente, observe o método OnPopulateMesh e substitua o método get de text.
Conseguiu, bom rapaz, verifica-se que só falta alterar o código fonte.

O absurdo realmente acabou, vamos começar ~

texto

Aqui está a parte lógica do código real

Deixe-me falar sobre a lógica dessa abordagem primeiro.
1. Quando o texto é inserido, os dois métodos SetVerticesDirty e SetLayoutDirty serão acionados para notificar a camada inferior de que as informações de renderização atual estão sujas e, em seguida, OnPopulateMesh será acionado ativamente.
2. Modifique o método get de text e corrija novamente o m_Text que deveria ter sido retornado. Mas não modifique o próprio m_Text para evitar a poluição de seus próprios dados.
3. A lógica da revisão é remover primeiro a parte de <icon=XXX> com expressões regulares e substituí-la por um número apropriado de espaços. Em seguida, insira as propriedades da imagem na lista.
4. Crie um número correspondente de imagens e modifique a exibição por meio das propriedades das imagens.

Em seguida, fale sobre o método de escrita de texto e gráficos mistos

This script can show <icon name=xxx size=40 x=0 y=0/> and string.

insira a descrição da imagem aqui

variáveis ​​privadas de script

public class MixedPicText : Text
{
    
    
    //空格的编码(只读)
    private static readonly string replaceStr = "\u00A0";
    //图片部分的正则表达式(只读)
    private static readonly Regex imageTagRegex = new Regex(@"<icon name=([^>\s]+)([^>]*)/>");//(名字)(属性)
    //图片属性的正则表达式(只读)
    private static readonly Regex imageParaRegex = new Regex(@"(\w+)=([^\s]+)");//(key)=(value)
    //图片属性类列表
    private List<RichTextImageInfo> imageInfoList = new List<RichTextImageInfo>();
    //是否图片有变化的Dirty
    private bool isImageDirty = false;
    //顶点信息,是在计算图片位置时需要的
    private IList<UIVertex> verts = null;
    //当前已经创建的图片
    private List<RectTransform> showImageList = new List<RectTransform>();
}

Classe de atributo de imagem

public class MixedPicTextImageInfo
{
    
    
    public string name;       //名字(路径)
    public Vector2 position;  //位置
    public int startVertex;   //起始顶点
    public int vertexLength;  //占据顶点数

    //标签属性
    public int size = 40;    //尺寸
    public float offsetX = 0f;  //X偏移
    public float offsetY = 0f;  //Y偏移

    public void SetValue(string key, string value)
    {
    
    
        switch (key)
        {
    
    
            case "size":
                {
    
    
                    int.TryParse(value, out size);
                    break;
                }
            case "x":
                {
    
    
                    float.TryParse(value, out offsetX);
                    break;
                }
            case "y":
                {
    
    
                    float.TryParse(value, out offsetY);
                    break;
                }
            default:
                break;
        }
    }
}

Então, vamos falar sobre como desmontar

    protected string CalculateLayoutWithImage(string richText)
    {
    
    
    	//获取填充文本的生成设置
        Vector2 extents = rectTransform.rect.size;
        var settings = GetGenerationSettings(extents);
        //计算空格的宽度
        float unitsPerPixel = 1 / pixelsPerUnit;
        float spaceWidth = cachedTextGenerator.GetPreferredWidth(replaceStr, settings) * unitsPerPixel;

        //解析图片标签,并将标签替换为空格
        imageInfoList.Clear();
        Match match = null;
        //创建一个StringBuilder,用来存储替换后的字符串
        StringBuilder builder = new StringBuilder();
        while ((match = imageTagRegex.Match(richText)).Success)
        {
    
    
            //拆解正则数据
            MixedPicTextImageInfo imageInfo = new MixedPicTextImageInfo();
            imageInfo.name = match.Groups[1].Value;
            string paras = match.Groups[2].Value;
            if (!string.IsNullOrEmpty(paras))
            {
    
    
                //拆解图片的属性
                var keyValueCollection = imageParaRegex.Matches(paras);
                for (int i = 0; i < keyValueCollection.Count; i++)
                {
    
    
                    string key = keyValueCollection[i].Groups[1].Value;
                    string value = keyValueCollection[i].Groups[2].Value;
                    imageInfo.SetValue(key, value);
                }
            }
            imageInfo.startVertex = match.Index * 4;
            //占据几个空格 一般图片和文字间需要间距 所以多一个空格
            int num = Mathf.CeilToInt(imageInfo.size / spaceWidth) + 1;
            imageInfo.vertexLength = num * 4;
            imageInfoList.Add(imageInfo);
            
            //将字符串数据添加
            builder.Length = 0;
            builder.Append(richText, 0, match.Index);
            for (int i = 0; i < num; i++)
            {
    
    
                builder.Append(replaceStr);
            }
            builder.Append(richText, match.Index + match.Length, richText.Length - match.Index - match.Length);
            richText = builder.ToString();
        }

        //用新的文本来构成数据,获取新的顶点信息等
        cachedTextGenerator.Populate(richText, settings);
        verts = cachedTextGenerator.verts;
        int vertCount = verts.Count;

        //计算图片位置
        for (int i = imageInfoList.Count - 1; i >= 0; i--)
        {
    
    
            MixedPicTextImageInfo imageInfo = imageInfoList[i];

            int charIndex = imageInfo.startVertex / 4;
            string str = richText.Substring(0, charIndex);
            int newLine = str.Split('\n').Length - 1;
            int whiteSpace = str.Split(' ').Length - 1;
            int indexOfTextQuad = (charIndex * 4) - newLine * 4 - whiteSpace * 4;
            if (indexOfTextQuad < vertCount) {
    
    
                Vector2 pos = (verts[indexOfTextQuad].position +
                    verts[indexOfTextQuad + 1 + imageInfo.vertexLength - 4].position +
                    verts[indexOfTextQuad + 2 + imageInfo.vertexLength - 4].position +
                    verts[indexOfTextQuad + 3].position) / 4f;
                
                //计算位置的缩放,在Canvas的RenderMode为ScreenSpaceCamera的情况下,不同分辨率的位置修正
                float posScale = 1f;
#if UNITY_EDITOR
                if (Application.isPlaying)
                {
    
    
                	//todo UICanvas是我自己项目中的,就是获取canvas而已,根据自己需求来改
                    ---
                    posScale = UICanvas.Get().canvas.scaleFactor;
                    ---
                }
                else
                {
    
    
                    if (canvas != null)
                    {
    
    
                        posScale = canvas.scaleFactor;
                    }
                }
#else
                posScale = UICanvas.Get().canvas.scaleFactor;
#endif
                pos /= posScale;//适应不同分辨率的屏幕
                pos += new Vector2(imageInfo.offsetX + spaceWidth / 2, imageInfo.size * 0.3f + imageInfo.offsetY);

                imageInfo.position = pos;
            } else {
    
    
                imageInfoList.RemoveAt(i);
            }
        }

		//此时认为图片有变化
        isImageDirty = true;

        return richText;
    }

O método de correção da string está pronto, então a instalação pode começar.

    public override string text {
    
    
        get
        {
    
    
        	//覆写text的get方法,直接返回修正后的字符串。不要动m_Text,会导致数据修改且不可逆
            return CalculateLayoutWithImage(m_Text);
        }
        set => base.text = value;
    }

Em seguida, vamos falar sobre a geração de imagens. O artigo original está colocado no Update, não sei porque, e não tentei colocá-lo em outros lugares, então vou mantê-lo como está.

    protected void Update()
    {
    
    
        if (isImageDirty)
        {
    
    
            isImageDirty = false;

			//用池的方法来创建图片
            RectTransform imgTrans;
            Image imageComp;
            for (int i = 0; i < imageInfoList.Count; i++)
            {
    
    
                if (i < showImageList.Count)
                {
    
    
                    imgTrans = showImageList[i];
                    imgTrans.gameObject.SetActive(true);
                    imageComp = imgTrans.GetComponent<Image>();
                }
                else
                {
    
    
                    imgTrans = new GameObject("Image", typeof(RectTransform)).transform as RectTransform;
                    imgTrans.SetParent(transform);
                    imageComp = imgTrans.gameObject.AddComponent<Image>();
                    showImageList.Add(imgTrans);
                }

                MixedPicTextImageInfo imageInfo = imageInfoList[i];
                imgTrans.localScale = Vector3.one;
                
                //todo 这里直接使用自己项目中的加载图片的方法
				---
				imageComp.sprite = sprite;
				---
                
                imageComp.SetNativeSize();
                imageComp.raycastTarget = false;
                imgTrans.localScale = Vector3.one * imageInfo.size / imgTrans.rect.width;
                imgTrans.anchoredPosition = imageInfo.position;
            }

            for (int i = imageInfoList.Count; i < showImageList.Count; i++)
            {
    
    
                showImageList[i].gameObject.SetActive(false);
            }
        }
    }
}

Finalmente, existem dois métodos de substituição

	//Awake的时候会触发SetDirty,所以先将原有的数据都清理掉,防止污染
    protected override void Awake()
    {
    
    
        base.Awake();
        showImageList.Clear();
        for (int i = transform.childCount - 1; i >= 0; i--)
        {
    
    
#if UNITY_EDITOR
            if (Application.isPlaying)
            {
    
    
#endif
                Destroy(transform.GetChild(i).gameObject);
#if UNITY_EDITOR
            }
            else
            {
    
    
                DestroyImmediate(transform.GetChild(i).gameObject);
            }
#endif
        }
    }
	//这个方法是在编辑模式下的处理,不然打开预制体的时候,因为Awake的原因,图片被清理掉了。
    protected override void Start()
    {
    
    
        base.Start();
        OnPopulateMesh(new VertexHelper());
    }

epílogo

Isso só foi modificado ontem. Na verdade, haverá outros problemas. Precisa ser verificado novamente.
Se houver novos problemas, voltarei e os mudarei.
insira a descrição da imagem aqui

Acho que você gosta

Origin blog.csdn.net/qql7267/article/details/122223641
Recomendado
Clasificación