diff --git a/flixel/animation/FlxAnimationController.hx b/flixel/animation/FlxAnimationController.hx index 26f43dad64..513c253d9a 100644 --- a/flixel/animation/FlxAnimationController.hx +++ b/flixel/animation/FlxAnimationController.hx @@ -725,8 +725,8 @@ class FlxAnimationController implements IFlxDestroyable { final name = frames[0].name; final postIndex = name.indexOf(".", prefix.length); - final postFix = name.substring(postIndex == -1 ? name.length : postIndex, name.length); - FlxFrame.sort(frames, prefix.length, postFix.length); + final suffix = name.substring(postIndex == -1 ? name.length : postIndex, name.length); + FlxFrame.sortFrames(frames, prefix, suffix); for (frame in frames) { diff --git a/flixel/graphics/frames/FlxFrame.hx b/flixel/graphics/frames/FlxFrame.hx index 89da585025..74f9f1e20f 100644 --- a/flixel/graphics/frames/FlxFrame.hx +++ b/flixel/graphics/frames/FlxFrame.hx @@ -4,6 +4,7 @@ import openfl.display.BitmapData; import openfl.geom.Point; import openfl.geom.Rectangle; import flixel.graphics.FlxGraphic; +import flixel.math.FlxMath; import flixel.math.FlxMatrix; import flixel.math.FlxPoint; import flixel.math.FlxRect; @@ -32,29 +33,67 @@ class FlxFrame implements IFlxDestroyable * Temp matrix helper, used internally */ static var _matrix = new FlxMatrix(); - + /** - * Sorts an array of `FlxFrame` objects by their name, e.g. - * `["tiles-001.png", "tiles-003.png", "tiles-002.png"]` - * with `"tiles-".length == prefixLength` and `".png".length == postfixLength`. + * Sorts frames based on the value of the frames' name between the prefix and suffix. + * Uses `Std.parseInt` to parse the value, if the result is `null`, 0 is used, if the result + * is a negative number, the absolute valute is used. + * + * @param frames The list of frames to sort + * @param prefix Everything in the frames' name *before* the order + * @param suffix Everything in the frames' name *after* the order + * @param warn Whether to warn on invalid names */ - public static function sort(frames:Array, prefixLength:Int, postfixLength:Int):Void + public static inline function sortFrames(frames:Array, prefix:String, ?suffix:String, warn = true):Void { - ArraySort.sort(frames, sortByName.bind(_, _, prefixLength, postfixLength)); + sortHelper(frames, prefix.length, suffix == null ? 0 : suffix.length, warn); } - - public static function sortByName(frame1:FlxFrame, frame2:FlxFrame, prefixLength:Int, postfixLength:Int):Int + + /** + * Sorts frames based on the value of the frames' name between the prefix and suffix. + * Uses `Std.parseInt` to parse the value, if the result is `null`, 0 is used, if the result + * is a negative number, the absolute valute is used. + * + * @param frames The list of frames to sort + * @param prefix Everything in the frames' name *before* the order + * @param suffix Everything in the frames' name *after* the order + * @param warn Whether to warn on invalid names + */ + public static function sort(frames:Array, prefixLength:Int, suffixLength:Int, warn = true):Void { - var name1:String = frame1.name; - var name2:String = frame2.name; - var num1:Null = Std.parseInt(name1.substring(prefixLength, name1.length - postfixLength)); - var num2:Null = Std.parseInt(name2.substring(prefixLength, name2.length - postfixLength)); - if (num1 == null) - num1 = 0; - if (num2 == null) - num2 = 0; - - return num1 - num2; + sortHelper(frames, prefixLength, suffixLength, warn); + } + + static function sortHelper(frames:Array, prefixLength:Int, suffixLength:Int, warn = true):Void + { + if (warn) + { + for (frame in frames) + checkValidName(frame.name, prefixLength, suffixLength); + } + + ArraySort.sort(frames, sortByName.bind(_, _, prefixLength, suffixLength)); + } + + static inline function checkValidName(name:String, prefixLength:Int, suffixLength:Int) + { + final nameSub = name.substring(prefixLength, name.length - suffixLength); + final num:Null = Std.parseInt(nameSub); + if (num == null) + FlxG.log.warn('Could not parse frame number of "$nameSub" in frame named "$name"'); + else if (num < 0) + FlxG.log.warn('Found negative frame number "$nameSub" in frame named "$name"'); + } + + public static function sortByName(frame1:FlxFrame, frame2:FlxFrame, prefixLength:Int, suffixLength:Int):Int + { + inline function getNameOrder(name:String):Int + { + final num:Null = Std.parseInt(name.substring(prefixLength, name.length - suffixLength)); + return if (num == null) 0 else FlxMath.absInt(num); + } + + return getNameOrder(frame1.name) - getNameOrder(frame2.name); } public var name:String; diff --git a/flixel/graphics/frames/FlxTileFrames.hx b/flixel/graphics/frames/FlxTileFrames.hx index 5549713a38..ae209f74b2 100644 --- a/flixel/graphics/frames/FlxTileFrames.hx +++ b/flixel/graphics/frames/FlxTileFrames.hx @@ -237,9 +237,9 @@ class FlxTileFrames extends FlxFramesCollection { var name:String = framesToAdd[0].name; var postIndex:Int = name.indexOf(".", Prefix.length); - var postFix:String = name.substring(postIndex == -1 ? name.length : postIndex, name.length); + var suffix:String = name.substring(postIndex == -1 ? name.length : postIndex, name.length); - FlxFrame.sort(framesToAdd, Prefix.length, postFix.length); + FlxFrame.sortFrames(framesToAdd, Prefix, suffix); return FlxTileFrames.fromFrames(framesToAdd); } diff --git a/flixel/graphics/frames/bmfont/BMFontChar.hx b/flixel/graphics/frames/bmfont/BMFontChar.hx index 3a19c8a47e..216f485b2d 100644 --- a/flixel/graphics/frames/bmfont/BMFontChar.hx +++ b/flixel/graphics/frames/bmfont/BMFontChar.hx @@ -39,8 +39,8 @@ class BMFontChar xoffset: charNode.att.intSafe("xoffset", 0), yoffset: charNode.att.intSafe("yoffset", 0), xadvance: charNode.att.intSafe("xadvance", 0), - page: charNode.att.intWarn("page", -1), - chnl: charNode.att.intWarn("chnl", -1), + page: charNode.att.intSafe("page", -1), + chnl: charNode.att.intSafe("chnl", -1), letter: charNode.att.stringSafe("letter") }; } diff --git a/flixel/graphics/frames/bmfont/BMFontCommon.hx b/flixel/graphics/frames/bmfont/BMFontCommon.hx index f02ec06343..47d7df60cc 100644 --- a/flixel/graphics/frames/bmfont/BMFontCommon.hx +++ b/flixel/graphics/frames/bmfont/BMFontCommon.hx @@ -29,7 +29,7 @@ class BMFontCommon return { lineHeight: commonNode.att.int("lineHeight"), - base: commonNode.att.intWarn("base", -1), + base: commonNode.att.intSafe("base", -1), scaleW: commonNode.att.intWarn("scaleW", 1), scaleH: commonNode.att.intWarn("scaleH", 1), pages: commonNode.att.intSafe("pages", 0), diff --git a/flixel/graphics/frames/bmfont/BMFontInfo.hx b/flixel/graphics/frames/bmfont/BMFontInfo.hx index 84050b8566..3cc673abef 100644 --- a/flixel/graphics/frames/bmfont/BMFontInfo.hx +++ b/flixel/graphics/frames/bmfont/BMFontInfo.hx @@ -30,21 +30,27 @@ class BMFontInfo static function fromXml(infoNode:BMFontXml):BMFontInfo { - return { + final info:BMFontInfo = + { face: infoNode.att.string("face"), size: infoNode.att.int("size"), bold: infoNode.att.boolSafe("bold", false), italic: infoNode.att.boolSafe("italic", false), smooth: infoNode.att.boolSafe("smooth", false), - charset: infoNode.att.stringWarn("charset"), - unicode: infoNode.att.boolWarn("unicode", false), - stretchH: infoNode.att.intWarn("stretchH", 100), - aa: infoNode.att.intWarn("aa", 1), - padding: BMFontPadding.fromString(infoNode.att.stringWarn("padding")), - spacing: BMFontSpacing.fromString(infoNode.att.stringWarn("spacing")), + charset: infoNode.att.stringSafe("charset"), + unicode: infoNode.att.boolSafe("unicode", false), + stretchH: infoNode.att.intSafe("stretchH", 100), + aa: infoNode.att.intSafe("aa", 1), outline: infoNode.att.intSafe("outline", 0), fixedHeight: infoNode.att.boolSafe("fixedHeight", false) } + + if (infoNode.has("padding")) + info.padding = BMFontPadding.fromString(infoNode.att.string("padding")); + if (infoNode.has("spacing")) + info.spacing = BMFontSpacing.fromString(infoNode.att.string("spacing")); + + return info; } static function fromText(infoText:String):BMFontInfo diff --git a/flixel/graphics/frames/bmfont/BMFontXml.hx b/flixel/graphics/frames/bmfont/BMFontXml.hx index 61dde396c5..a8321bc687 100644 --- a/flixel/graphics/frames/bmfont/BMFontXml.hx +++ b/flixel/graphics/frames/bmfont/BMFontXml.hx @@ -62,7 +62,7 @@ abstract BMFontXml(Xml) from Xml if (this.nodeType == Xml.Document) throw 'Cannot access document attribute $name'; - return this.nodeType == Xml.Document && this.exists(name); + return this.nodeType != Xml.Document && this.exists(name); } /** Check the existence of a sub node with the given name. **/ diff --git a/flixel/text/FlxText.hx b/flixel/text/FlxText.hx index 69a632b397..e010d537b3 100644 --- a/flixel/text/FlxText.hx +++ b/flixel/text/FlxText.hx @@ -59,6 +59,13 @@ class FlxText extends FlxSprite */ public var size(get, set):Int; + /** + * A number representing the amount of space that is uniformly distributed + * between all characters. The value specifies the number of pixels that are + * added to the advance after each character. + */ + public var letterSpacing(get, set):Float; + /** * The font used for this text (assuming that it's using embedded font). */ @@ -219,6 +226,7 @@ class FlxText extends FlxSprite textField.multiline = true; textField.wordWrap = true; _defaultFormat = new TextFormat(null, Size, 0xffffff); + letterSpacing = 0; font = FlxAssets.FONT_DEFAULT; _formatAdjusted = new TextFormat(); textField.defaultTextFormat = _defaultFormat; @@ -640,6 +648,18 @@ class FlxText extends FlxSprite return Size; } + inline function get_letterSpacing():Float + { + return _defaultFormat.letterSpacing; + } + + function set_letterSpacing(LetterSpacing:Float):Float + { + _defaultFormat.letterSpacing = LetterSpacing; + updateDefaultFormat(); + return LetterSpacing; + } + override function set_color(Color:FlxColor):Int { if (_defaultFormat.color == Color.to24Bit()) diff --git a/tests/unit/src/flixel/graphics/frames/bmfont/BMFontTest.hx b/tests/unit/src/flixel/graphics/frames/bmfont/BMFontTest.hx index 68e4558ef8..ae86b5e886 100644 --- a/tests/unit/src/flixel/graphics/frames/bmfont/BMFontTest.hx +++ b/tests/unit/src/flixel/graphics/frames/bmfont/BMFontTest.hx @@ -8,22 +8,22 @@ class BMFontTest extends FlxTest @Test function testTextFormat() { - var text = 'info face="Arial Black" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,2,3,4 spacing=2,1 outline=0 -common lineHeight=32 base=25 scaleW=256 scaleH=256 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 -page id=0 file="arial_black_0.png" -chars count=3 -char id=64 x=0 y=0 width=25 height=24 xoffset=-5 yoffset=7 xadvance=17 page=0 chnl=15 -char id=65 x=27 y=0 width=26 height=21 xoffset=-5 yoffset=7 xadvance=18 page=0 chnl=15 -char id=84 x=55 y=0 width=23 height=21 xoffset=-4 yoffset=7 xadvance=16 page=0 chnl=15 -kernings count=2 -kerning first=84 second=65 amount=-2 -kerning first=65 second=84 amount=-2 -'; - + var text = + 'info face="Arial Black" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,2,3,4 spacing=2,1 outline=0' + + '\ncommon lineHeight=32 base=25 scaleW=256 scaleH=256 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0' + + '\npage id=0 file="arial_black_0.png"' + + '\nchars count=3' + + '\nchar id=64 x=0 y=0 width=25 height=24 xoffset=-5 yoffset=7 xadvance=17 page=0 chnl=15' + + '\nchar id=65 x=27 y=0 width=26 height=21 xoffset=-5 yoffset=7 xadvance=18 page=0 chnl=15' + + '\nchar id=84 x=55 y=0 width=23 height=21 xoffset=-4 yoffset=7 xadvance=16 page=0 chnl=15' + + '\nkernings count=2' + + '\nkerning first=84 second=65 amount=-2 ' + + '\nkerning first=65 second=84 amount=-2 '; + var font = BMFont.parse(cast text); assertFont(font); } - + @Test function testXMLFormat() { @@ -44,11 +44,11 @@ kerning first=65 second=84 amount=-2 '; - + var font = BMFont.parse(cast xml); assertFont(font); } - + @Test function testBinaryFormat() { @@ -58,15 +58,14 @@ kerning first=65 second=84 amount=-2 "00019001800FBFF07001100000F410000001B0000001A001500FBFF07001200000F5" + "40000003700000017001500FCFF07001000000F05140000005400000041000000FEF" + "F4100000054000000FEFF"); - + var font = BMFont.parse(cast binary); assertFont(font); } - + // This assumes the incoming font has a specific configuration we are checking for - private function assertFont(font:BMFont) { - trace(font); - + private function assertFont(font:BMFont) + { // INFO Assert.areEqual("Arial Black", font.info.face); Assert.areEqual(32, font.info.size); @@ -84,7 +83,7 @@ kerning first=65 second=84 amount=-2 Assert.areEqual(2, font.info.spacing.x); Assert.areEqual(1, font.info.spacing.y); Assert.areEqual(0, font.info.outline); - + // COMMON Assert.areEqual(32, font.common.lineHeight); Assert.areEqual(25, font.common.base); @@ -96,81 +95,40 @@ kerning first=65 second=84 amount=-2 Assert.areEqual(0, font.common.redChnl); Assert.areEqual(0, font.common.greenChnl); Assert.areEqual(0, font.common.blueChnl); - + // PAGES Assert.areEqual(1, font.pages.length); Assert.areEqual(0, font.pages[0].id); Assert.areEqual("arial_black_0.png", font.pages[0].file); - + // Chars Assert.areEqual(3, font.chars.length); - var expectedChars:Array = [ - { - id: 64, - x: 0, - y: 0, - width: 25, - height: 24, - xoffset: -5, - yoffset: 7, - xadvance: 17, - page: 0, - chnl: 15, - letter: "@" - }, - { - id: 65, - x: 27, - y: 0, - width: 26, - height: 21, - xoffset: -5, - yoffset: 7, - xadvance: 18, - page: 0, - chnl: 15, - letter: "A" - }, - { - id: 84, - x: 55, - y: 0, - width: 23, - height: 21, - xoffset: -4, - yoffset: 7, - xadvance: 16, - page: 0, - chnl: 15, - letter: "T" - }, + var expectedChars:Array = + [ + { id: 64, x: 0, y: 0, width: 25, height: 24, xoffset: -5, yoffset: 7, xadvance: 17, page: 0, chnl: 15, letter: "@" }, + { id: 65, x: 27, y: 0, width: 26, height: 21, xoffset: -5, yoffset: 7, xadvance: 18, page: 0, chnl: 15, letter: "A" }, + { id: 84, x: 55, y: 0, width: 23, height: 21, xoffset: -4, yoffset: 7, xadvance: 16, page: 0, chnl: 15, letter: "T" }, ]; - - for (i in 0...expectedChars.length) { + + for (i in 0...expectedChars.length) assertCharMatches(expectedChars[i], font.chars[i]); - } - + // Kerning Assert.areEqual(font.chars.length, 3); - var expectedKerns:Array = [ - { - first: 84, - second: 65, - amount: -2 - }, - { - first: 65, - second: 84, - amount: -2 - }, + var expectedKerns:Array = + [ + { first: 84, second: 65, amount: -2 }, + { first: 65, second: 84, amount: -2 }, ]; - - for (i in 0...expectedKerns.length) { + + for (i in 0...expectedKerns.length) + { assertKerningMatches(expectedKerns[i], font.kerning[i]); } } - - private function assertCharMatches(expected:BMFontChar, actual:BMFontChar) { + + private function assertCharMatches(expected:BMFontChar, actual:BMFontChar) + { Assert.areEqual(expected.id, actual.id); Assert.areEqual(expected.x, actual.x); Assert.areEqual(expected.y, actual.y); @@ -181,12 +139,12 @@ kerning first=65 second=84 amount=-2 Assert.areEqual(expected.xadvance, actual.xadvance); Assert.areEqual(expected.page, actual.page); Assert.areEqual(expected.chnl, actual.chnl); - // if (expected.letter != null) { + // if (expected.letter != null) // Assert.areEqual(expected.letter, actual.letter); - // } } - - private function assertKerningMatches(expected:BMFontKerning, actual:BMFontKerning) { + + private function assertKerningMatches(expected:BMFontKerning, actual:BMFontKerning) + { Assert.areEqual(expected.first, actual.first); Assert.areEqual(expected.second, actual.second); Assert.areEqual(expected.amount, actual.amount);