From 0404b94e5eaca74384294f291914da1343ff9c92 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 18 Sep 2014 11:56:49 -0700 Subject: [PATCH] WebGLSprites of all types no longer display anything at all if their required data hasn't been rendered. --- app/lib/surface/WebGLLayer.coffee | 12 +++++---- app/lib/surface/WebGLSprite.coffee | 23 +++++++++++++--- test/app/lib/surface/SpriteBoss.spec.coffee | 29 ++++++++++++++++++--- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/app/lib/surface/WebGLLayer.coffee b/app/lib/surface/WebGLLayer.coffee index 871147fc4..74306dfe7 100644 --- a/app/lib/surface/WebGLLayer.coffee +++ b/app/lib/surface/WebGLLayer.coffee @@ -103,6 +103,13 @@ module.exports = class WebGLLayer extends CocoClass async ?= @buildAsync builder = new createjs.SpriteSheetBuilder() groups = _.groupBy(@toRenderBundles, ((bundle) -> @renderGroupingKey(bundle.thangType, '', bundle.colorConfig)), @) + + # Always have an empty frame be the first frame. + # Then if you go to a frame that DNE, it doesn't show up. + emptiness = new createjs.Container() + emptiness.setBounds(0, 0, 1, 1) + builder.addFrame(emptiness) + for bundleGrouping in _.values(groups) thangType = bundleGrouping[0].thangType colorConfig = bundleGrouping[0].colorConfig @@ -116,11 +123,6 @@ module.exports = class WebGLLayer extends CocoClass else @renderRasterImage(thangType, builder) - if not _.size(groups) - emptiness = new createjs.Container() - emptiness.setBounds(0, 0, 1, 1) - builder.addFrame(emptiness) - if async builder.buildAsync() builder.on 'complete', @onBuildSpriteSheetComplete, @, true, builder diff --git a/app/lib/surface/WebGLSprite.coffee b/app/lib/surface/WebGLSprite.coffee index b4233c592..c51fcc1a1 100644 --- a/app/lib/surface/WebGLSprite.coffee +++ b/app/lib/surface/WebGLSprite.coffee @@ -47,6 +47,9 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer @regY = -reg.y * scale func = if @paused then 'gotoAndStop' else 'gotoAndPlay' animationName = @spriteSheetPrefix + actionName + if not (animationName in @spriteSheet.getAnimations()) + @singleChildSprite.gotoAndStop(0) + return @singleChildSprite[func](animationName) @singleChildSprite.framerate = action.framerate or 20 @@ -58,6 +61,9 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer @regY = -reg.y @childMovieClips = [] @baseMovieClip = @buildMovieClip(action.animation) + if not @baseMovieClip + @children = [] + return @children = @baseMovieClip.children @frames = action.frames @frames = (parseInt(f) for f in @frames.split(',')) if @frames @@ -73,6 +79,9 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer @regX = -reg.x * scale @regY = -reg.y * scale animationName = @spriteSheetPrefix + actionName + if not (animationName in @spriteSheet.getAnimations()) + @singleChildSprite.gotoAndStop(0) + return @singleChildSprite.gotoAndStop(animationName) else @@ -80,6 +89,9 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer @regY = -reg.y @childMovieClips = [] containerName = @spriteSheetPrefix + action.container + if not (containerName in @spriteSheet.getAnimations()) + @children = [] + return sprite = new createjs.Sprite(@spriteSheet) sprite.gotoAndStop(containerName) sprite.scaleX = sprite.scaleY = 1 / @resolutionFactor @@ -93,8 +105,10 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer movieClip = new createjs.MovieClip() locals = {} - _.extend locals, @buildMovieClipContainers(animData.containers) - _.extend locals, @buildMovieClipAnimations(animData.animations) + _.extend locals, containers = @buildMovieClipContainers(animData.containers) + return false if not containers + _.extend locals, animations = @buildMovieClipAnimations(animData.animations) + return false if not animations toSkip = {} toSkip[shape.bn] = true for shape in animData.shapes @@ -127,7 +141,9 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer outerContainer = new createjs.SpriteContainer(@spriteSheet) innerContainer = new createjs.Sprite(@spriteSheet) innerContainer.scaleX = innerContainer.scaleY = 1 / @resolutionFactor - innerContainer.gotoAndStop(@spriteSheetPrefix + localContainer.gn) + animationName = @spriteSheetPrefix + localContainer.gn + return false if not (animationName in @spriteSheet.getAnimations()) + innerContainer.gotoAndStop(animationName) outerContainer.addChild(innerContainer) outerContainer.setTransform(localContainer.t...) outerContainer._off = localContainer.o if localContainer.o? @@ -139,6 +155,7 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer map = {} for localAnimation in localAnimations animation = @buildMovieClip(localAnimation.gn, localAnimation.a...) + return false if not animation animation.setTransform(localAnimation.t...) map[localAnimation.bn] = animation @childMovieClips.push(animation) diff --git a/test/app/lib/surface/SpriteBoss.spec.coffee b/test/app/lib/surface/SpriteBoss.spec.coffee index afcfe439c..80f9df502 100644 --- a/test/app/lib/surface/SpriteBoss.spec.coffee +++ b/test/app/lib/surface/SpriteBoss.spec.coffee @@ -11,6 +11,7 @@ describe 'SpriteBoss', -> spriteBoss = null canvas = null stage = null + midRenderExpectations = [] # bit of a hack to move tests which happen mid-initialization into a separate test # This suite just creates and renders the stage once, and then has each of the tests # check the resulting data for the whole thing, without changing anything. @@ -61,10 +62,16 @@ describe 'SpriteBoss', -> # Don't have the layer automatically draw for move_fore, instead have it notified from WebGLSprites that # this animation or its containers are needed. -# defaultLayer.setDefaultActions(_.without defaultLayer.defaultActions, 'move_fore') + defaultLayer.setDefaultActions(_.without defaultLayer.defaultActions, 'move_fore') # Render the simple world with just trees spriteBoss.update(true) + + # Test that the unrendered, static sprites aren't showing anything + midRenderExpectations.push([spriteBoss.sprites['Tree 1'].imageObject.children.length,0,'static container action']) + midRenderExpectations.push([spriteBoss.sprites['Tree 2'].imageObject.children[0].currentFrame,0,'static spriteSheet action']) + midRenderExpectations.push([spriteBoss.sprites['Tree 2'].imageObject.children[0].paused,true,'static spriteSheet action']) + defaultLayer.once 'new-spritesheet', -> # Now make the world a little more complicated. @@ -76,8 +83,8 @@ describe 'SpriteBoss', -> {id: 'Ogre S', spriteName: 'Ogre Munchkin M', exists: true, pos: {x:0, y:-8}, action: 'move', health: 5, maxHealth: 10, rotation: Math.PI/2, acts: true } # Set ogres side by side with different render strategies - {id: 'FROgre', spriteName: 'Full Render Ogre', exists: true, pos: {x:-10, y:-8}, action: 'idle', health: 10, maxHealth: 10, rotation: 0, acts: true, alpha: 0.5 } - {id: 'NotFROgre', spriteName: 'Ogre Munchkin M', exists: true, pos: {x:-8, y:-8}, action: 'idle', health: 10, maxHealth: 10, rotation: 0, acts: true } + {id: 'FROgre', spriteName: 'Full Render Ogre', exists: true, pos: {x:-10, y:-8}, action: 'move', health: 10, maxHealth: 10, rotation: -Math.PI/2, acts: true, alpha: 0.5 } + {id: 'NotFROgre', spriteName: 'Ogre Munchkin M', exists: true, pos: {x:-8, y:-8}, action: 'move', health: 10, maxHealth: 10, rotation: -Math.PI/2, acts: true } # A line of ogres overlapping to test child ordering {id: 'Ogre 1', spriteName: 'Ogre Munchkin M', exists: true, pos: {x:-14, y:0}, action: 'die', health: 5, maxHealth: 10, rotation: 0, acts: true } @@ -89,10 +96,18 @@ describe 'SpriteBoss', -> {id: 'Fangrider 1', spriteName: 'Ogre Fangrider', exists: true, pos: {x:8, y:8}, action: 'move', health: 20, maxHealth: 20, rotation: 0, acts: true } ] - _.find(world.thangs, {id: 'Tree Will Disappear'}).exists = false + _.find(world.thangs, {id: 'Tree Will Disappear'}).exists = false world.thangMap[thang.id] = thang for thang in world.thangs spriteBoss.update(true) + + # Test that the unrendered, animated sprites aren't showing anything + midRenderExpectations.push([spriteBoss.sprites['NotFROgre'].imageObject.children.length,0,'animated container action']) + midRenderExpectations.push([spriteBoss.sprites['FROgre'].imageObject.children[0].currentFrame,0,'animated spriteSheet action']) + midRenderExpectations.push([spriteBoss.sprites['FROgre'].imageObject.children[0].paused,true,'animated spriteSheet action']) + defaultLayer.once 'new-spritesheet', -> + expect(spriteBoss.sprites['Ogre 1'].imageObject.children.length).toBeGreaterThan(0) +# expect(spriteBoss.sprites['FROgre'].imageObject.children[0].currentFrame).toBeGreaterThan(0) done() # showMe() # Uncomment to display this world when you run any of these tests. @@ -113,6 +128,12 @@ describe 'SpriteBoss', -> createjs.Ticker.addEventListener "tick", listener $('body').append($('
')) + it 'does not display anything for sprites whose animations or containers have not been rendered yet', -> + for expectation in midRenderExpectations + if expectation[0] isnt expectation[1] + console.error('This type of action display failed:', expectation[2]) + expect(expectation[0]).toBe(expectation[1]) + it 'rotates and animates sprites according to thang rotation', -> expect(spriteBoss.sprites['Ogre N'].imageObject.currentAnimation).toBe('move_fore') expect(spriteBoss.sprites['Ogre E'].imageObject.currentAnimation).toBe('move_side')