Пример #1
0
        // ------------------------------------------------------------------
        // Desc:
        // ------------------------------------------------------------------

        private static float BuildLine(string _text, exISpriteFont _sprite, ref int _parsedVbIndex, float _top)
        {
            // cache property
            var    letterSpacing = _sprite.letterSpacing;
            var    wordSpacing   = _sprite.wordSpacing;
            bool   useKerning    = _sprite.useKerning;
            exFont font          = _sprite.font;
            //
            Vector2 pos         = new Vector2(0.0f, _top);
            float   lastAdvance = 0.0f;
            float   lastWidth   = 0.0f;

            for (int _charIndex = 0; _charIndex < _text.Length; ++_charIndex, _parsedVbIndex += 4, pos.x += letterSpacing)
            {
                char c = _text[_charIndex];

                // if new line
                if (c == '\n' || c == '\r')
                {
                    vertices.buffer[_parsedVbIndex + 0] = new Vector3();
                    vertices.buffer[_parsedVbIndex + 1] = new Vector3();
                    vertices.buffer[_parsedVbIndex + 2] = new Vector3();
                    vertices.buffer[_parsedVbIndex + 3] = new Vector3();
                    ++_charIndex;
                    _parsedVbIndex += 4;
                    break;
                }

                bool hasPreviousChar = _charIndex > 0;
                if (hasPreviousChar)
                {
                    pos.x += lastAdvance;
                    // kerning
                    if (useKerning)
                    {
                        pos.x += font.GetKerning(_text[_charIndex - 1], c);
                    }
                    // wordSpacing
                    if (c == ' ')
                    {
                        pos.x += wordSpacing;
                    }
                }

                float width, advance;
                if (BuildChar(font, c, pos, _parsedVbIndex, out width, out advance))
                {
                    // advance x
                    lastWidth   = width;
                    lastAdvance = advance;
                }
            }
            return(pos.x + lastWidth);
        }
Пример #2
0
    public static void DoInspectorGUI(exSpriteBaseInspector _inspector,
                                      SerializedProperty _textProp,
                                      SerializedProperty _textAlignProp,
                                      SerializedProperty _useKerningProp,
                                      SerializedProperty _wrapWordProp,
                                      SerializedProperty _lineHeightProp,
                                      SerializedProperty _customLineHeightProp,
                                      SerializedProperty _letterSpacingProp,
                                      SerializedProperty _wordSpacingProp,
                                      SerializedProperty _topColorProp,
                                      SerializedProperty _botColorProp)
    {
        _inspector.customSizeProp.boolValue = true;
        {
            // font
            exISpriteFont sp = _inspector.serializedObject.targetObject as exISpriteFont;
            if (sp != null)
            {
                EditorGUI.BeginChangeCheck();
                exFont.TypeForEditor fontType = (exFont.TypeForEditor)EditorGUILayout.EnumPopup("Font Type", sp.fontType);
                int oldFontSize = sp.fontSize;
                if (EditorGUI.EndChangeCheck())
                {
                    sp.fontType = fontType;
                    if (fontType == exFont.TypeForEditor.Dynamic)
                    {
                        if (sp.dynamicFont == null)
                        {
                            sp.SetFont(Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font);
                        }
                        sp.fontSize = oldFontSize;
                    }
                    EditorUtility.SetDirty(sp as Object);
                    sp.lineHeight = sp.fontSize;        // 自动重设行高
                }
                EditorGUI.indentLevel++;
                if (fontType == exFont.TypeForEditor.Bitmap)
                {
                    EditorGUI.BeginChangeCheck();
                    exBitmapFont font = EditorGUILayout.ObjectField("Font", sp.bitmapFont, typeof(exBitmapFont), false) as exBitmapFont;
                    if (EditorGUI.EndChangeCheck())
                    {
                        sp.SetFont(font);
                        EditorUtility.SetDirty(sp as Object);
                        sp.lineHeight = sp.fontSize;    // 自动重设行高
                    }
                }
                else
                {
                    EditorGUI.BeginChangeCheck();
                    Font font = EditorGUILayout.ObjectField("Font", sp.dynamicFont, typeof(Font), false) as Font;
                    if (EditorGUI.EndChangeCheck())
                    {
                        sp.SetFont(font);
                        EditorUtility.SetDirty(sp as Object);
                        sp.lineHeight = sp.fontSize;    // 自动重设行高
                    }
                    EditorGUI.BeginChangeCheck();
                    var fontStyle = (FontStyle)EditorGUILayout.EnumPopup("Font Style", sp.fontStyle);
                    var fontSize  = EditorGUILayout.IntField("Font Size", sp.fontSize);
                    //sp.lineHeight = EditorGUILayout.IntField("Line Height", sp.lineHeight);
                    if (EditorGUI.EndChangeCheck())
                    {
                        sp.fontStyle = fontStyle;
                        sp.fontSize  = fontSize;
                        EditorUtility.SetDirty(sp as Object);
                    }
                }
                EditorGUI.indentLevel--;
            }
        }

        // text
        EditorGUI.BeginChangeCheck();
        EditorGUILayout.PropertyField(_textProp, new GUIContent("Text"));
        if (EditorGUI.EndChangeCheck())
        {
            foreach (Object obj in _inspector.serializedObject.targetObjects)
            {
                exISpriteFont sp = obj as exISpriteFont;
                if (sp != null)
                {
                    sp.text = _textProp.stringValue;
                    EditorUtility.SetDirty(sp as Object);
                }
            }
        }

        // textAlign
        EditorGUI.BeginChangeCheck();
        EditorGUILayout.PropertyField(_textAlignProp, new GUIContent("Text Align"));
        if (EditorGUI.EndChangeCheck())
        {
            foreach (Object obj in _inspector.serializedObject.targetObjects)
            {
                exISpriteFont sp = obj as exISpriteFont;
                if (sp != null)
                {
                    sp.textAlign = (TextAlignment)_textAlignProp.enumValueIndex;
                    EditorUtility.SetDirty(sp as Object);
                }
            }
        }

        // useKerning
        EditorGUI.BeginChangeCheck();
        EditorGUILayout.PropertyField(_useKerningProp, new GUIContent("Use Kerning"));
        if (EditorGUI.EndChangeCheck())
        {
            foreach (Object obj in _inspector.serializedObject.targetObjects)
            {
                exISpriteFont sp = obj as exISpriteFont;
                if (sp != null)
                {
                    sp.useKerning = _useKerningProp.boolValue;
                    EditorUtility.SetDirty(obj);
                }
            }
        }

        // wrap word
        EditorGUI.BeginChangeCheck();
        EditorGUILayout.PropertyField(_wrapWordProp, new GUIContent("Wrap Word"), true);
        if (EditorGUI.EndChangeCheck())
        {
            foreach (Object obj in _inspector.serializedObject.targetObjects)
            {
                exISpriteFont sp = obj as exISpriteFont;
                if (sp != null)
                {
                    sp.wrapWord = _wrapWordProp.boolValue;
                    EditorUtility.SetDirty(obj);
                }
            }
        }

        // custom line height
        EditorGUI.BeginChangeCheck();
        EditorGUILayout.PropertyField(_customLineHeightProp, new GUIContent("Custom Line Height"), true);
        if (EditorGUI.EndChangeCheck())
        {
            foreach (Object obj in _inspector.serializedObject.targetObjects)
            {
                exISpriteFont sp = obj as exISpriteFont;
                if (sp != null)
                {
                    sp.customLineHeight = _customLineHeightProp.boolValue;
                    EditorUtility.SetDirty(obj);
                }
            }
        }

        if (_customLineHeightProp.boolValue)
        {
            EditorGUI.indentLevel++;

            // line height
            EditorGUI.BeginChangeCheck();
            EditorGUILayout.PropertyField(_lineHeightProp, new GUIContent("Line Height"), true);
            if (EditorGUI.EndChangeCheck())
            {
                foreach (Object obj in _inspector.serializedObject.targetObjects)
                {
                    exISpriteFont sp = obj as exISpriteFont;
                    if (sp != null)
                    {
                        sp.lineHeight = _lineHeightProp.intValue;
                        EditorUtility.SetDirty(obj);
                    }
                }
            }

            EditorGUI.indentLevel--;
        }

        // letter spacing
        EditorGUI.BeginChangeCheck();
        EditorGUILayout.PropertyField(_letterSpacingProp, new GUIContent("Letter Spacing"), true);
        if (EditorGUI.EndChangeCheck())
        {
            foreach (Object obj in _inspector.serializedObject.targetObjects)
            {
                exISpriteFont sp = obj as exISpriteFont;
                if (sp != null)
                {
                    sp.letterSpacing = _letterSpacingProp.intValue;
                    EditorUtility.SetDirty(obj);
                }
            }
        }

        // word spacing
        EditorGUI.BeginChangeCheck();
        EditorGUILayout.PropertyField(_wordSpacingProp, new GUIContent("Word Spacing"), true);
        if (EditorGUI.EndChangeCheck())
        {
            foreach (Object obj in _inspector.serializedObject.targetObjects)
            {
                exISpriteFont sp = obj as exISpriteFont;
                if (sp != null)
                {
                    sp.wordSpacing = _wordSpacingProp.intValue;
                    EditorUtility.SetDirty(obj);
                }
            }
        }

        // topColor
        EditorGUI.BeginChangeCheck();
        EditorGUILayout.PropertyField(_topColorProp, new GUIContent("Top Color"), true);
        if (EditorGUI.EndChangeCheck())
        {
            foreach (Object obj in _inspector.serializedObject.targetObjects)
            {
                exISpriteFont sp = obj as exISpriteFont;
                if (sp != null)
                {
                    sp.topColor = _topColorProp.colorValue;
                    EditorUtility.SetDirty(obj);
                }
            }
        }

        // botColor
        EditorGUI.BeginChangeCheck();
        EditorGUILayout.PropertyField(_botColorProp, new GUIContent("Bot Color"), true);
        if (EditorGUI.EndChangeCheck())
        {
            foreach (Object obj in _inspector.serializedObject.targetObjects)
            {
                exISpriteFont sp = obj as exISpriteFont;
                if (sp != null)
                {
                    sp.botColor = _botColorProp.colorValue;
                    EditorUtility.SetDirty(obj);
                }
            }
        }
    }
Пример #3
0
        // ------------------------------------------------------------------
        /// Return used vertex count
        // ------------------------------------------------------------------

        private static int BuildTextInLocalSpace(exISpriteFont _sprite, out float maxWidth)
        {
            if ((_sprite.updateFlags & exUpdateFlags.Color) == 0)
            {
                colors32 = null;    // no need to update color
            }
            //texelSize = new Vector2();
            if ((_sprite.updateFlags & exUpdateFlags.UV) == 0)
            {
                uvs = null;         // no need to update uv
            }
            //else if (uvs != null && _sprite.font.texture != null) {
            //    texelSize = _sprite.font.texture.texelSize;
            //}

            float halfLineHeightMargin = (_sprite.lineHeight - _sprite.fontSize) * 0.5f;

            _sprite.height = halfLineHeightMargin;
            maxWidth       = 0;

            // NOTE: 这里不用考虑预分配的空间用不完的情况,因为大部分情况下只需输出单行文本。
            //       而且一个mesh其实也显示不了太多字。
            StringBuilder strBuilder    = new StringBuilder(_sprite.text.Length);
            int           cur_index     = 0;
            int           parsedVbIndex = vbIndex;
            bool          finished      = false;

            while (finished == false)
            {
                int line_width = 0;
                strBuilder.Length = 0;  // Clear
                exTextUtility.CalcTextLine(out line_width,
                                           out cur_index,
                                           strBuilder,
                                           _sprite.text,
                                           cur_index,
                                           (int)_sprite.width,
                                           _sprite.font,
                                           _sprite.wordSpacing,
                                           _sprite.letterSpacing,
                                           _sprite.wrapWord,
                                           false,
                                           false);
                int   lineStart = parsedVbIndex;
                float lineWidth = BuildLine(strBuilder.ToString(), _sprite, ref parsedVbIndex, -_sprite.height);
                if (lineWidth > maxWidth)
                {
                    maxWidth = lineWidth;
                }

                // text alignment
                switch (_sprite.textAlign)
                {
                case TextAlignment.Left:
                    // top left
                    break;

                case TextAlignment.Center:
                    // convert to top center
                    float halfLineWidth = lineWidth * 0.5f;
                    for (int i = lineStart; i < parsedVbIndex; ++i)
                    {
                        vertices.buffer[i].x -= halfLineWidth;
                    }
                    break;

                case TextAlignment.Right:
                    // convert to top right
                    for (int i = lineStart; i < parsedVbIndex; ++i)
                    {
                        vertices.buffer[i].x -= lineWidth;
                    }
                    break;
                }
                _sprite.height += _sprite.lineHeight;
                finished        = (cur_index >= _sprite.text.Length);
            }

            _sprite.height += halfLineHeightMargin;

            return(parsedVbIndex - vbIndex);
        }
Пример #4
0
        // ------------------------------------------------------------------
        // Desc:
        // ------------------------------------------------------------------

        public static void BuildText(exISpriteFont _sprite, Space _space, exList <Vector3> _vertices, int _vbIndex, exList <Vector2> _uvs = null, exList <Color32> _colors32 = null)
        {
            // 保存变量到全局,减少内部传参
            vertices = _vertices;
            uvs      = _uvs;
            colors32 = _colors32;
            vbIndex  = _vbIndex;

            // request font texture, this may called OnFontTextureRebuilt to set _sprite.updateFlags
            _sprite.font.RequestCharactersInTexture(_sprite.text);
            _sprite.height = 0.0f;
            float displayWidth       = 0; // 实际渲染的宽度不等于sprite.width(换行宽度)
            int   visibleVertexCount = 0;

            if (_sprite.font.isValid)
            {
                visibleVertexCount = BuildTextInLocalSpace(_sprite, out displayWidth);
            }
            if (visibleVertexCount == 0 && _sprite.vertexCount >= 4)
            {
                visibleVertexCount = 4;
                // 放四个假的点,用于计算后续变换,才能得出正确的boundingbox
                _vertices.buffer[_vbIndex + 0] = new Vector3();
                _vertices.buffer[_vbIndex + 1] = new Vector3();
                _vertices.buffer[_vbIndex + 2] = new Vector3();
                _vertices.buffer[_vbIndex + 3] = new Vector3();
            }
            int invisibleVertexStart = _vbIndex + visibleVertexCount;

            // calculate anchor and offset
            Vector2 anchorOffset = new Vector2();

            // convert to top left
            switch (_sprite.textAlign)
            {
            case TextAlignment.Left:
                break;

            case TextAlignment.Center:
                anchorOffset.x = displayWidth * 0.5f;
                break;

            case TextAlignment.Right:
                anchorOffset.x = displayWidth;
                break;
            }
            // convert anchor from top center to user defined
            switch (_sprite.anchor)
            {
            case Anchor.TopLeft:                                        anchorOffset.y = 0.0f;                  break;

            case Anchor.TopCenter: anchorOffset.x -= displayWidth * 0.5f; anchorOffset.y = 0.0f;                  break;

            case Anchor.TopRight: anchorOffset.x -= displayWidth;        anchorOffset.y = 0.0f;                  break;

            case Anchor.MidLeft:                                        anchorOffset.y = _sprite.height * 0.5f; break;

            case Anchor.MidCenter: anchorOffset.x -= displayWidth * 0.5f; anchorOffset.y = _sprite.height * 0.5f; break;

            case Anchor.MidRight: anchorOffset.x -= displayWidth;        anchorOffset.y = _sprite.height * 0.5f; break;

            case Anchor.BotLeft:                                        anchorOffset.y = _sprite.height;        break;

            case Anchor.BotCenter: anchorOffset.x -= displayWidth * 0.5f; anchorOffset.y = _sprite.height;        break;

            case Anchor.BotRight: anchorOffset.x -= displayWidth;        anchorOffset.y = _sprite.height;        break;

            default: anchorOffset.x -= displayWidth * 0.5f; anchorOffset.y = _sprite.height * 0.5f; break;
            }
            // offset
            Vector3 offset = anchorOffset + _sprite.offset;

            Vector2 shearOffset = _sprite.shear;

            if (shearOffset.x != 0)
            {
                shearOffset.x = _sprite.GetScaleY(_space) * shearOffset.x;
            }
            if (shearOffset.y != 0)
            {
                shearOffset.y = _sprite.GetScaleX(_space) * shearOffset.y;
            }
            for (int i = _vbIndex; i < invisibleVertexStart; i += 4)
            {
                Vector3 v0 = _vertices.buffer[i + 0];
                Vector3 v1 = _vertices.buffer[i + 1];
                Vector3 v2 = _vertices.buffer[i + 2];
                Vector3 v3 = _vertices.buffer[i + 3];
                // apply anchor and offset
                v0 += offset;
                v1 += offset;
                v2 += offset;
                v3 += offset;
                // shear
                if (shearOffset.x != 0)
                {
                    float halfCharHeight = (v1.y - v0.y) * 0.5f;
                    float topOffset      = shearOffset.x * (halfCharHeight + anchorOffset.y);
                    float botOffset      = shearOffset.x * (-halfCharHeight + anchorOffset.y);
                    v0.x += botOffset;
                    v1.x += topOffset;
                    v2.x += topOffset;
                    v3.x += botOffset;
                }
                if (shearOffset.y != 0)
                {
                    float halfCharWidth = (v2.y - v0.y) * 0.5f;
                    float leftOffset    = shearOffset.y * (-halfCharWidth + anchorOffset.x);
                    float rightOffset   = shearOffset.y * (halfCharWidth + anchorOffset.x);
                    v0.y += leftOffset;
                    v1.y += leftOffset;
                    v2.y += rightOffset;
                    v3.y += rightOffset;
                }
                // transform
                if (_space == Space.World)
                {
                    v0   = _sprite.cachedWorldMatrix.MultiplyPoint3x4(v0);
                    v1   = _sprite.cachedWorldMatrix.MultiplyPoint3x4(v1);
                    v2   = _sprite.cachedWorldMatrix.MultiplyPoint3x4(v2);
                    v3   = _sprite.cachedWorldMatrix.MultiplyPoint3x4(v3);
                    v0.z = 0; v1.z = 0; v2.z = 0; v3.z = 0;
                }
                _vertices.buffer[i + 0] = v0;
                _vertices.buffer[i + 1] = v1;
                _vertices.buffer[i + 2] = v2;
                _vertices.buffer[i + 3] = v3;
            }
            bool hasPivot = invisibleVertexStart > _vbIndex;

            if (hasPivot)
            {
                // hide invisible vertex 防止撑大boundingbox
                int vbEnd = _vbIndex + _sprite.vertexCount;
                for (int i = invisibleVertexStart; i < vbEnd; i += 4)
                {
    #if UNITY_EDITOR
                    if (i >= _vertices.buffer.Length)
                    {
                        Debug.Log(string.Format("[BuildText|exSpriteFont] i: {0} vbEnd: " + vbEnd + " _vertices: " + _vertices.buffer.Length, i));
                    }
    #endif
                    _vertices.buffer[i + 0] = _vertices.buffer[_vbIndex];
                    _vertices.buffer[i + 1] = _vertices.buffer[_vbIndex];
                    _vertices.buffer[i + 2] = _vertices.buffer[_vbIndex];
                    _vertices.buffer[i + 3] = _vertices.buffer[_vbIndex];
                }
            }

            vertices = null;
            uvs      = null;
            colors32 = null;

            // TODO: pixel-perfect
        }
Пример #5
0
        // ------------------------------------------------------------------
        // Desc:
        // ------------------------------------------------------------------

        public static exUpdateFlags UpdateBuffers(exISpriteFont _sprite, Space _space, float _alpha,
                                                  exList <Vector3> _vertices, exList <Vector2> _uvs, exList <Color32> _colors32, exList <int> _indices, int _vbIndex, int _ibIndex)
        {
#if UNITY_EDITOR
            if (_sprite.text == null || _sprite.vertexCount < _sprite.text.Length * exMesh.QUAD_VERTEX_COUNT)
            {
                Debug.LogError("顶点缓冲长度不够,是否绕开属性直接修改了text_?: " + _sprite.vertexCount, _sprite as Object);
                return(_sprite.updateFlags);
            }
#endif
            bool colorUpdated = false;   // 有渐变色时,每个字符单独计算顶点色,否则全局填充顶点色

            // Debug.Log(string.Format("[UpdateBuffers|SpriteFontBuilder] _vbIndex: {0} _ibIndex: {1}", _vbIndex, _ibIndex));
            if ((_sprite.updateFlags & exUpdateFlags.Text) != 0)
            {
                if (_alpha > 0.0f)
                {
                    // 初始化渐变色要用到的全局参数
                    Color tmp = _sprite.color; tmp.a *= _alpha;
                    topFinalColor = _sprite.topColor * tmp;
                    botFinalColor = _sprite.botColor * tmp;
                    if (_sprite.fontSize != 0)
                    {
                        botFinalColor = exMath.Lerp(topFinalColor, botFinalColor, 1.0f / _sprite.fontSize);   // 预除
                    }
                    else
                    {
                        botFinalColor = topFinalColor;
                        Debug.LogWarning("Failed to use gradient font color due to invalid font size", _sprite as Object);
                    }
                }
                else
                {
                    topFinalColor = new Color();
                    botFinalColor = new Color();
                }
                colorUpdated = topFinalColor != botFinalColor;
                if ((_sprite.updateFlags & exUpdateFlags.Text & ~exUpdateFlags.Color) != 0 || colorUpdated)
                {
                    BuildText(_sprite, _space, _vertices, _vbIndex, _uvs, colorUpdated ? _colors32 : null);
                }
            }
            if ((_sprite.updateFlags & exUpdateFlags.Index) != 0 && _indices != null)
            {
                // update index buffer
                int indexBufferEnd = _ibIndex + _sprite.indexCount - 5;
                for (int i = _ibIndex, v = _vbIndex; i < indexBufferEnd; i += 6, v += 4)
                {
                    _indices.buffer[i]     = v;
                    _indices.buffer[i + 1] = v + 1;
                    _indices.buffer[i + 2] = v + 2;
                    _indices.buffer[i + 3] = v + 2;
                    _indices.buffer[i + 4] = v + 3;
                    _indices.buffer[i + 5] = v;
                }
            }
            if ((_sprite.updateFlags & exUpdateFlags.Color) != 0 && _colors32 != null && colorUpdated == false)
            {
                // if not gradient we just need to fill same color here
                Color   tmp             = _sprite.topColor * _sprite.color;
                Color32 color32         = new Color(tmp.r, tmp.g, tmp.b, tmp.a * _alpha);
                int     vertexBufferEnd = _vbIndex + _sprite.text.Length * 4;
                for (int i = _vbIndex; i < vertexBufferEnd; ++i)
                {
                    _colors32.buffer[i] = color32;
                }
            }
            exUpdateFlags updatedFlags = _sprite.updateFlags;
            _sprite.updateFlags = exUpdateFlags.None;
            return(updatedFlags);
        }