diff --git a/app/application.coffee b/app/application.coffee index 549d1822c..abe2df04f 100644 --- a/app/application.coffee +++ b/app/application.coffee @@ -11,6 +11,7 @@ marked.setOptions {gfm: true, sanitize: true, smartLists: true, breaks: false} # TODO, add C-style macro constants like this? window.SPRITE_RESOLUTION_FACTOR = 3 +window.SPRITE_PLACEHOLDER_RADIUS = 30 # Prevent Ctrl/Cmd + [ / ], P, S ctrlDefaultPrevented = [219, 221, 80, 83] diff --git a/app/lib/surface/WebGLLayer.coffee b/app/lib/surface/WebGLLayer.coffee index 32f5098e2..97a71611c 100644 --- a/app/lib/surface/WebGLLayer.coffee +++ b/app/lib/surface/WebGLLayer.coffee @@ -3,6 +3,8 @@ CocoClass = require 'lib/CocoClass' WebGLSprite = require './WebGLSprite' {SpriteContainerLayer} = require 'lib/surface/Layer' +NEVER_RENDER_ANYTHING = true # set to true to test placeholders + module.exports = class WebGLLayer extends CocoClass _.extend(WebGLLayer.prototype, Backbone.Events) @@ -109,12 +111,13 @@ module.exports = class WebGLLayer extends CocoClass 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) + # The first frame is always the 'loading', ie placeholder, image. + placeholder = @createPlaceholder() + dimension = @resolutionFactor*SPRITE_PLACEHOLDER_RADIUS*2 + placeholder.setBounds(0, 0, dimension, dimension) + builder.addFrame(placeholder) + groups = {} if NEVER_RENDER_ANYTHING for bundleGrouping in _.values(groups) thangType = bundleGrouping[0].thangType colorConfig = bundleGrouping[0].colorConfig @@ -135,6 +138,17 @@ module.exports = class WebGLLayer extends CocoClass sheet = builder.build() @onBuildSpriteSheetComplete(null, builder) return sheet + + createPlaceholder: -> + # TODO: Experiment with this. Perhaps have rectangles if default layer is obstacle or floor, + # and different colors for different layers. + g = new createjs.Graphics() + g.setStrokeStyle(5) + g.beginStroke(createjs.Graphics.getRGB(64,64,64)) + g.beginFill(createjs.Graphics.getRGB(64,64,64,0.7)) + radius = @resolutionFactor*SPRITE_PLACEHOLDER_RADIUS + g.drawCircle(radius, radius, radius) + new createjs.Shape(g) onBuildSpriteSheetComplete: (e, builder) -> return if @initializing diff --git a/app/lib/surface/WebGLSprite.coffee b/app/lib/surface/WebGLSprite.coffee index 6a7cdf318..1c7b354ff 100644 --- a/app/lib/surface/WebGLSprite.coffee +++ b/app/lib/surface/WebGLSprite.coffee @@ -33,6 +33,7 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer goto: (actionName, @paused=true) -> @currentAnimation = actionName @baseMovieClip = @framerate = null + @actionNotSupported = false action = @thangType.getActions()[actionName] randomStart = actionName.startsWith('move') @@ -47,13 +48,21 @@ 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) - @notifyActionNeedsRender(action) - return @singleChildSprite[func](animationName) + if @singleChildSprite.currentFrame is 0 + @regX = -reg.x + @regY = -reg.y + @singleChildSprite.stop() + @notifyActionNeedsRender(action) + bounds = @thangType.get('raw').animations[action.animation].bounds + @singleChildSprite.x = bounds[0] + @singleChildSprite.y = bounds[1] + console.log 'bounds?', bounds + @singleChildSprite.scaleX = bounds[2] / (SPRITE_PLACEHOLDER_RADIUS * @resolutionFactor * 2) + @singleChildSprite.scaleY = bounds[3] / (SPRITE_PLACEHOLDER_RADIUS * @resolutionFactor * 2) + return + @singleChildSprite.framerate = action.framerate or 20 - if randomStart and frames = @spriteSheet.getAnimation(animationName)?.frames @singleChildSprite.currentAnimationFrame = Math.floor(Math.random() * frames.length) @@ -62,10 +71,6 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer @regY = -reg.y @childMovieClips = [] @baseMovieClip = @buildMovieClip(action.animation) - if not @baseMovieClip - @children = [] - @notifyActionNeedsRender(action) - return @children = @baseMovieClip.children @frames = action.frames @frames = (parseInt(f) for f in @frames.split(',')) if @frames @@ -74,31 +79,41 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer @baseMovieClip.gotoAndStop(@currentFrame) @loop = action.loops isnt false @goesTo = action.goesTo + @notifyActionNeedsRender(action) if @actionNotSupported if action.container + scale = @resolutionFactor * (action.scale ? @thangType.get('scale') ? 1) + if @singleChildSprite - scale = @resolutionFactor * (action.scale ? @thangType.get('scale') ? 1) @regX = -reg.x * scale @regY = -reg.y * scale animationName = @spriteSheetPrefix + actionName - if not (animationName in @spriteSheet.getAnimations()) - @singleChildSprite.gotoAndStop(0) - @notifyActionNeedsRender(action) - return @singleChildSprite.gotoAndStop(animationName) + if @singleChildSprite.currentFrame is 0 + @notifyActionNeedsRender(action) + bounds = @thangType.get('raw').containers[action.container].b + @singleChildSprite.x = bounds[0] + @singleChildSprite.y = bounds[1] + @singleChildSprite.scaleX = bounds[2] / (SPRITE_PLACEHOLDER_RADIUS * 2) + @singleChildSprite.scaleY = bounds[3] / (SPRITE_PLACEHOLDER_RADIUS * 2) + return else @regX = -reg.x @regY = -reg.y @childMovieClips = [] containerName = @spriteSheetPrefix + action.container - if not (containerName in @spriteSheet.getAnimations()) - @children = [] - @notifyActionNeedsRender(action) - return sprite = new createjs.Sprite(@spriteSheet) sprite.gotoAndStop(containerName) - sprite.scaleX = sprite.scaleY = 1 / @resolutionFactor + if sprite.currentFrame is 0 + @notifyActionNeedsRender(action) + bounds = @thangType.get('raw').containers[action.container].b + sprite.x = bounds[0] + sprite.y = bounds[1] + sprite.scaleX = bounds[2] / (SPRITE_PLACEHOLDER_RADIUS * 2 * scale) + sprite.scaleY = bounds[3] / (SPRITE_PLACEHOLDER_RADIUS * 2 * scale) + else + sprite.scaleX = sprite.scaleY = 1 / scale @children = [sprite] return @@ -112,10 +127,8 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer movieClip = new createjs.MovieClip() locals = {} - _.extend locals, containers = @buildMovieClipContainers(animData.containers) - return false if not containers - _.extend locals, animations = @buildMovieClipAnimations(animData.animations) - return false if not animations + _.extend locals, @buildMovieClipContainers(animData.containers) + _.extend locals, @buildMovieClipAnimations(animData.animations) toSkip = {} toSkip[shape.bn] = true for shape in animData.shapes @@ -147,10 +160,16 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer for localContainer in localContainers outerContainer = new createjs.SpriteContainer(@spriteSheet) innerContainer = new createjs.Sprite(@spriteSheet) - innerContainer.scaleX = innerContainer.scaleY = 1 / (@resolutionFactor * (@thangType.get('scale') or 1)) - animationName = @spriteSheetPrefix + localContainer.gn - return false if not (animationName in @spriteSheet.getAnimations()) - innerContainer.gotoAndStop(animationName) + innerContainer.gotoAndStop(@spriteSheetPrefix + localContainer.gn) + if innerContainer.currentFrame is 0 + @actionNotSupported = true + bounds = @thangType.get('raw').containers[localContainer.gn].b + innerContainer.x = bounds[0] + innerContainer.y = bounds[1] + innerContainer.scaleX = bounds[2] / (SPRITE_PLACEHOLDER_RADIUS * @resolutionFactor * 2) + innerContainer.scaleY = bounds[3] / (SPRITE_PLACEHOLDER_RADIUS * @resolutionFactor * 2) + else + innerContainer.scaleX = innerContainer.scaleY = 1 / (@resolutionFactor * (@thangType.get('scale') or 1)) outerContainer.addChild(innerContainer) outerContainer.setTransform(localContainer.t...) outerContainer._off = localContainer.o if localContainer.o? @@ -162,7 +181,6 @@ 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 98663b5c3..82fcb6904 100644 --- a/test/app/lib/surface/SpriteBoss.spec.coffee +++ b/test/app/lib/surface/SpriteBoss.spec.coffee @@ -69,7 +69,9 @@ describe 'SpriteBoss', -> 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 1'].imageObject.children.length,1,'static container action']) + midRenderExpectations.push([spriteBoss.sprites['Tree 1'].imageObject.children[0].currentFrame,0,'static container action']) + midRenderExpectations.push([spriteBoss.sprites['Tree 1'].imageObject.children[0].paused,true,'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']) @@ -102,13 +104,15 @@ describe 'SpriteBoss', -> 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['NotFROgre'].imageObject.children.length,10,'animated container action']) + for child in spriteBoss.sprites['NotFROgre'].imageObject.children + midRenderExpectations.push([child.children[0].currentFrame, 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', -> + showMe() # Uncomment to display this world when you run any of these tests. done() -# showMe() # Uncomment to display this world when you run any of these tests. beforeEach (done) -> init(done) diff --git a/test/app/lib/surface/WebGLSprite.spec.coffee b/test/app/lib/surface/WebGLSprite.spec.coffee index e239d6067..72b34bc72 100644 --- a/test/app/lib/surface/WebGLSprite.spec.coffee +++ b/test/app/lib/surface/WebGLSprite.spec.coffee @@ -56,6 +56,7 @@ describe 'WebGLSprite', -> expect(webGLSprite.paused).toBe(false) webGLSprite.gotoAndStop('move_fore') expect(webGLSprite.paused).toBe(true) + showMe() it 'has a tick function which moves the animation forward', -> webGLSprite.gotoAndPlay('attack')