mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Initial integration of SpriteBoss with the WebGLLayer and all else.
This commit is contained in:
parent
1ef3d150a8
commit
7387248ffd
9 changed files with 310 additions and 125 deletions
|
@ -92,7 +92,6 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
else
|
||||
@actions = @thangType.getActions()
|
||||
@createMarks()
|
||||
@queueAction 'idle'
|
||||
|
||||
@updateBaseScale()
|
||||
@scaleFactorX = @thang.scaleFactorX if @thang?.scaleFactorX?
|
||||
|
@ -102,11 +101,16 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
|
||||
setImageObject: (newImageObject) ->
|
||||
if @imageObject
|
||||
@imageObject.removeAllEventListeners()
|
||||
@imageObject.destroy?()
|
||||
if parent = @imageObject.parent
|
||||
parent.removeChild @imageObject
|
||||
parent.addChild newImageObject
|
||||
parent.updateLayerOrder()
|
||||
|
||||
# get the cocosprite to update things
|
||||
for prop in ['lastPos', 'currentRootAction']
|
||||
delete @[prop]
|
||||
|
||||
@imageObject = newImageObject
|
||||
@configureMouse()
|
||||
@imageObject.on 'animationend', @playNextAction
|
||||
|
@ -536,6 +540,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
pos
|
||||
|
||||
createMarks: ->
|
||||
return # TODO: get marks working again
|
||||
return unless @options.camera
|
||||
if @thang
|
||||
allProps = []
|
||||
|
|
|
@ -16,25 +16,26 @@
|
|||
|
||||
** Grid lines--somewhere--we will figure it out, do not really need it at first
|
||||
###
|
||||
|
||||
layerClassProperties = {
|
||||
TRANSFORM_CHILD: 'child' # Layer transform is managed by its parents
|
||||
TRANSFORM_SURFACE: 'surface' # Layer moves/scales/zooms with the Surface of the World
|
||||
TRANSFORM_SURFACE_TEXT: 'surface_text' # Layer moves with the Surface but is size-independent
|
||||
TRANSFORM_SCREEN: 'screen' # Layer stays fixed to the screen (different from child?)
|
||||
}
|
||||
|
||||
module.exports = class Layer extends createjs.Container
|
||||
@TRANSFORM_CHILD = 'child' # Layer transform is managed by its parents
|
||||
@TRANSFORM_SURFACE = 'surface' # Layer moves/scales/zooms with the Surface of the World
|
||||
@TRANSFORM_SURFACE_TEXT = 'surface_text' # Layer moves with the Surface but is size-independent
|
||||
@TRANSFORM_SCREEN = 'screen' # Layer stays fixed to the screen (different from child?)
|
||||
|
||||
layerProperties = {
|
||||
subscriptions:
|
||||
'camera:zoom-updated': 'onZoomUpdated'
|
||||
|
||||
constructor: (options) ->
|
||||
super()
|
||||
@initialize()
|
||||
|
||||
initializeLayer: (options) ->
|
||||
options ?= {}
|
||||
@name = options.name ? 'Unnamed'
|
||||
@layerPriority = options.layerPriority ? 0
|
||||
@transformStyle = options.transform ? Layer.TRANSFORM_CHILD
|
||||
@transformStyle = options.transform ? layerClassProperties.TRANSFORM_CHILD
|
||||
@camera = options.camera
|
||||
console.error @toString(), 'needs a camera.' unless @camera
|
||||
console.debug @toString(), 'needs a camera.' unless @camera
|
||||
@updateLayerOrder = _.bind(@updateLayerOrder, @)
|
||||
@updateLayerOrder = _.throttle @updateLayerOrder, 1000 / 30 # Don't call multiple times in one frame; 30 FPS is probably good enough
|
||||
Backbone.Mediator.subscribe(channel, @[func], @) for channel, func of @subscriptions
|
||||
|
||||
|
@ -42,27 +43,12 @@ module.exports = class Layer extends createjs.Container
|
|||
child.destroy?() for child in @children
|
||||
Backbone.Mediator.unsubscribe(channel, @[func], @) for channel, func of @subscriptions
|
||||
delete @updateLayerOrder
|
||||
|
||||
|
||||
toString: -> "<Layer #{@layerPriority}: #{@name}>"
|
||||
|
||||
addChild: (children...) ->
|
||||
super children...
|
||||
if @transformStyle is Layer.TRANSFORM_SURFACE_TEXT
|
||||
for child in children
|
||||
child.scaleX /= @scaleX
|
||||
child.scaleY /= @scaleY
|
||||
|
||||
removeChild: (children...) ->
|
||||
super children...
|
||||
if @transformStyle is Layer.TRANSFORM_SURFACE_TEXT
|
||||
for child in children
|
||||
child.scaleX *= @scaleX
|
||||
child.scaleY *= @scaleY
|
||||
|
||||
updateLayerOrder: =>
|
||||
#console.log @, @toString(), 'sorting children', _.clone @children if @name is 'Default'
|
||||
|
||||
updateLayerOrder: ->
|
||||
@sortChildren @layerOrderComparator
|
||||
|
||||
|
||||
layerOrderComparator: (a, b) ->
|
||||
# Optimize
|
||||
alp = a.layerPriority or 0
|
||||
|
@ -85,21 +71,65 @@ module.exports = class Layer extends createjs.Container
|
|||
return 0 unless aPos and bPos
|
||||
return (bPos.y - aPos.y) or (bPos.x - aPos.x)
|
||||
return az - bz
|
||||
|
||||
|
||||
onZoomUpdated: (e) ->
|
||||
return unless e.camera is @camera
|
||||
if @transformStyle in [Layer.TRANSFORM_SURFACE, Layer.TRANSFORM_SURFACE_TEXT]
|
||||
# TODO: move this to the new SpriteStage, because it's what needs to be transformed.
|
||||
# This is a bit of a hack to get it to work for the time being.
|
||||
if @parent and @transformStyle is layerClassProperties.TRANSFORM_CHILD
|
||||
@parent.scaleX = @parent.scaleY = e.zoom
|
||||
@parent.regX = e.surfaceViewport.x
|
||||
@parent.regY = e.surfaceViewport.y
|
||||
|
||||
if @transformStyle in [layerClassProperties.TRANSFORM_SURFACE, layerClassProperties.TRANSFORM_SURFACE_TEXT]
|
||||
change = @scaleX / e.zoom
|
||||
@scaleX = @scaleY = e.zoom
|
||||
@regX = e.surfaceViewport.x
|
||||
@regY = e.surfaceViewport.y
|
||||
if @transformStyle is Layer.TRANSFORM_SURFACE_TEXT
|
||||
if @transformStyle is layerClassProperties.TRANSFORM_SURFACE_TEXT
|
||||
for child in @children
|
||||
child.scaleX *= change
|
||||
child.scaleY *= change
|
||||
|
||||
}
|
||||
|
||||
|
||||
class ContainerLayer extends createjs.Container
|
||||
constructor: (options) ->
|
||||
super()
|
||||
@initialize()
|
||||
@initializeLayer(options)
|
||||
|
||||
addChild: (children...) ->
|
||||
super children...
|
||||
if @transformStyle is ContainerLayer.TRANSFORM_SURFACE_TEXT
|
||||
for child in children
|
||||
child.scaleX /= @scaleX
|
||||
child.scaleY /= @scaleY
|
||||
|
||||
removeChild: (children...) ->
|
||||
super children...
|
||||
if @transformStyle is ContainerLayer.TRANSFORM_SURFACE_TEXT
|
||||
for child in children
|
||||
child.scaleX *= @scaleX
|
||||
child.scaleY *= @scaleY
|
||||
|
||||
cache: ->
|
||||
return unless @children.length
|
||||
bounds = @getBounds()
|
||||
return unless bounds
|
||||
super bounds.x, bounds.y, bounds.width, bounds.height, 2
|
||||
|
||||
class SpriteContainerLayer extends createjs.SpriteContainer
|
||||
constructor: (spriteSheet, options) ->
|
||||
super(spriteSheet)
|
||||
@initialize(spriteSheet)
|
||||
@initializeLayer(options)
|
||||
|
||||
_.extend(ContainerLayer.prototype, layerProperties)
|
||||
_.extend(SpriteContainerLayer.prototype, layerProperties)
|
||||
_.extend(ContainerLayer, layerClassProperties)
|
||||
_.extend(SpriteContainerLayer, layerClassProperties)
|
||||
|
||||
module.exports.ContainerLayer = ContainerLayer
|
||||
module.exports.SpriteContainerLayer = SpriteContainerLayer
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
CocoClass = require 'lib/CocoClass'
|
||||
{me} = require 'lib/auth'
|
||||
Layer = require './Layer'
|
||||
Layer = require './WebGLLayer'
|
||||
IndieSprite = require 'lib/surface/IndieSprite'
|
||||
WizardSprite = require 'lib/surface/WizardSprite'
|
||||
FlagSprite = require 'lib/surface/FlagSprite'
|
||||
|
@ -66,7 +66,7 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
['Floating', 10]
|
||||
]
|
||||
@spriteLayers[name] = new Layer name: name, layerPriority: priority, transform: Layer.TRANSFORM_CHILD, camera: @camera
|
||||
@surfaceLayer.addChild _.values(@spriteLayers)...
|
||||
@surfaceLayer.addChild (spriteLayer.spriteContainer for spriteLayer in _.values(@spriteLayers))...
|
||||
|
||||
layerForChild: (child, sprite) ->
|
||||
unless child.layerPriority?
|
||||
|
@ -77,7 +77,7 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
child.layerPriority ?= 0
|
||||
return @spriteLayers['Default'] unless child.layerPriority
|
||||
layer = _.findLast @spriteLayers, (layer, name) ->
|
||||
layer.layerPriority <= child.layerPriority
|
||||
layer.spriteContainer.layerPriority <= child.layerPriority
|
||||
layer ?= @spriteLayers['Land'] if child.layerPriority < -40
|
||||
layer ? @spriteLayers['Default']
|
||||
|
||||
|
@ -86,19 +86,19 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
console.error 'Sprite collision! Already have:', id if @sprites[id]
|
||||
@sprites[id] = sprite
|
||||
@spriteArray.push sprite
|
||||
sprite.imageObject.layerPriority ?= sprite.thang?.layerPriority
|
||||
layer ?= @spriteLayers['Obstacle'] if sprite.thang?.spriteName.search(/(dungeon|indoor).wall/i) isnt -1
|
||||
layer ?= @layerForChild sprite.imageObject, sprite
|
||||
layer.addChild sprite.imageObject
|
||||
layer.updateLayerOrder()
|
||||
layer.addCocoSprite sprite
|
||||
layer.spriteContainer.updateLayerOrder()
|
||||
sprite
|
||||
|
||||
createMarks: ->
|
||||
return # TODO: get these marks working again
|
||||
@targetMark = new Mark name: 'target', camera: @camera, layer: @spriteLayers['Ground'], thangType: 'target'
|
||||
@selectionMark = new Mark name: 'selection', camera: @camera, layer: @spriteLayers['Ground'], thangType: 'selection'
|
||||
|
||||
createSpriteOptions: (options) ->
|
||||
_.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers['Ground'], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers['Floating'], spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible
|
||||
_.extend options, camera: @camera, resolutionFactor: SPRITE_RESOLUTION_FACTOR, groundLayer: @spriteLayers['Ground'], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers['Floating'], spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible
|
||||
|
||||
createIndieSprites: (indieSprites, withWizards) ->
|
||||
unless @indieSprites
|
||||
|
@ -185,7 +185,7 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
@adjustSpriteExistence() if frameChanged
|
||||
sprite.update frameChanged for sprite in @spriteArray
|
||||
@updateSelection()
|
||||
@spriteLayers['Default'].updateLayerOrder()
|
||||
@spriteLayers['Default'].spriteContainer.updateLayerOrder()
|
||||
@cache()
|
||||
|
||||
adjustSpriteExistence: ->
|
||||
|
@ -230,24 +230,7 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
return itemsJustEquipped
|
||||
|
||||
cache: (update=false) ->
|
||||
return if @cached and not update
|
||||
wallSprites = (sprite for sprite in @spriteArray when sprite.thangType?.get('name').search(/(dungeon|indoor).wall/i) isnt -1)
|
||||
return if _.any (s.stillLoading for s in wallSprites)
|
||||
walls = (sprite.thang for sprite in wallSprites)
|
||||
@world.calculateBounds()
|
||||
wallGrid = new Grid walls, @world.size()...
|
||||
for wallSprite in wallSprites
|
||||
wallSprite.updateActionDirection wallGrid
|
||||
wallSprite.updateScale()
|
||||
wallSprite.updatePosition()
|
||||
#console.log @wallGrid.toString()
|
||||
@spriteLayers['Obstacle'].uncache() if @spriteLayers['Obstacle'].cacheID # might have changed sizes
|
||||
@spriteLayers['Obstacle'].cache()
|
||||
# test performance of doing land layer, too, to see if it's faster
|
||||
# @spriteLayers['Land'].uncache() if @spriteLayers['Land'].cacheID # might have changed sizes
|
||||
# @spriteLayers['Land'].cache()
|
||||
# I don't notice much difference - Scott
|
||||
@cached = true
|
||||
# TODO: remove caching
|
||||
|
||||
spriteFor: (thangID) -> @sprites[thangID]
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ AudioPlayer = require 'lib/AudioPlayer'
|
|||
{me} = require 'lib/auth'
|
||||
Camera = require './Camera'
|
||||
CameraBorder = require './CameraBorder'
|
||||
Layer = require './Layer'
|
||||
Layer = require('./Layer').ContainerLayer
|
||||
Letterbox = require './Letterbox'
|
||||
Dimmer = require './Dimmer'
|
||||
CountdownScreen = require './CountdownScreen'
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
SpriteBuilder = require 'lib/sprites/SpriteBuilder'
|
||||
CocoClass = require 'lib/CocoClass'
|
||||
WebGLSprite = require './WebGLSprite'
|
||||
{SpriteContainerLayer} = require 'lib/surface/Layer'
|
||||
|
||||
module.exports = class WebGLLayer extends CocoClass # createjs.SpriteContainer
|
||||
module.exports = class WebGLLayer extends CocoClass
|
||||
|
||||
_.extend(WebGLLayer.prototype, Backbone.Events)
|
||||
|
||||
|
@ -19,13 +20,15 @@ module.exports = class WebGLLayer extends CocoClass # createjs.SpriteContainer
|
|||
spriteSheet: null
|
||||
spriteContainer: null
|
||||
|
||||
constructor: ->
|
||||
super(arguments...)
|
||||
constructor: (@layerOptions) ->
|
||||
super()
|
||||
@initializing = true
|
||||
@spriteSheet = @_renderNewSpriteSheet(false) # builds an empty spritesheet
|
||||
@spriteContainer = new createjs.SpriteContainer(@spriteSheet)
|
||||
@spriteContainer = new SpriteContainerLayer(@spriteSheet, @layerOptions)
|
||||
@actionRenderState = {}
|
||||
@toRenderBundles = []
|
||||
@cocoSprites = []
|
||||
@initializing = false
|
||||
|
||||
setDefaultActions: (@defaultActions) ->
|
||||
|
||||
|
@ -63,6 +66,7 @@ module.exports = class WebGLLayer extends CocoClass # createjs.SpriteContainer
|
|||
@renderNewSpriteSheet()
|
||||
|
||||
addDefaultActionsToRender: (cocoSprite) ->
|
||||
needToRender = false
|
||||
if cocoSprite.thangType.get('raster')
|
||||
@upsertActionToRender(cocoSprite.thangType)
|
||||
else
|
||||
|
@ -72,11 +76,12 @@ module.exports = class WebGLLayer extends CocoClass # createjs.SpriteContainer
|
|||
|
||||
upsertActionToRender: (thangType, actionName, colorConfig) ->
|
||||
groupKey = @renderGroupingKey(thangType, actionName, colorConfig)
|
||||
return if @actionRenderState[groupKey] isnt undefined
|
||||
return false if @actionRenderState[groupKey] isnt undefined
|
||||
@actionRenderState[groupKey] = 'need-to-render'
|
||||
@toRenderBundles.push({thangType: thangType, actionName: actionName, colorConfig: colorConfig})
|
||||
return if @willRender or not @buildAutomatically
|
||||
return true if @willRender or not @buildAutomatically
|
||||
@willRender = _.defer => @renderNewSpriteSheet()
|
||||
return true
|
||||
|
||||
renderNewSpriteSheet: ->
|
||||
@willRender = false
|
||||
|
@ -109,11 +114,27 @@ module.exports = class WebGLLayer extends CocoClass # createjs.SpriteContainer
|
|||
builder.buildAsync()
|
||||
builder.on 'complete', @onBuildSpriteSheetComplete, @, true, builder
|
||||
else
|
||||
builder.build()
|
||||
sheet = builder.build()
|
||||
@onBuildSpriteSheetComplete(null, builder)
|
||||
return sheet
|
||||
|
||||
onBuildSpriteSheetComplete: (e, builder) ->
|
||||
console.log 'done?', builder.spriteSheet
|
||||
|
||||
return if @initializing
|
||||
@spriteSheet = builder.spriteSheet
|
||||
oldLayer = @spriteContainer
|
||||
@spriteContainer = new SpriteContainerLayer(@spriteSheet, @layerOptions)
|
||||
for cocoSprite in @cocoSprites
|
||||
@setImageObjectToCocoSprite(cocoSprite)
|
||||
for prop in ['scaleX', 'scaleY', 'regX', 'regY']
|
||||
@spriteContainer[prop] = oldLayer[prop]
|
||||
if parent = oldLayer.parent
|
||||
index = parent.getChildIndex(oldLayer)
|
||||
parent.removeChildAt(index)
|
||||
parent.addChildAt(@spriteContainer, index)
|
||||
@layerOptions.camera.updateZoom(true)
|
||||
@spriteContainer.updateLayerOrder()
|
||||
@trigger 'new-spritesheet'
|
||||
|
||||
renderContainers: (thangType, colorConfig, actionNames, spriteSheetBuilder) ->
|
||||
containersToRender = {}
|
||||
for actionName in actionNames
|
||||
|
@ -223,4 +244,6 @@ module.exports = class WebGLLayer extends CocoClass # createjs.SpriteContainer
|
|||
sprite.layerPriority = cocoSprite.thang?.layerPriority ? cocoSprite.thangType.get 'layerPriority'
|
||||
sprite.name = cocoSprite.thang?.spriteName or cocoSprite.thangType.get 'name'
|
||||
cocoSprite.addHealthBar()
|
||||
cocoSprite.setImageObject(sprite)
|
||||
cocoSprite.setImageObject(sprite)
|
||||
cocoSprite.update(true)
|
||||
@spriteContainer.addChild(sprite)
|
|
@ -1,13 +1,23 @@
|
|||
SpriteBuilder = require 'lib/sprites/SpriteBuilder'
|
||||
|
||||
module.exports = class WebGLSprite extends createjs.SpriteContainer
|
||||
childSpriteContainers: null
|
||||
childMovieClips: null
|
||||
|
||||
constructor: (@spriteSheet, @thangType, @spriteSheetPrefix) ->
|
||||
@initialize(@spriteSheet)
|
||||
if @thangType.get('renderStrategy') isnt 'container'
|
||||
@singleChildSprite = new createjs.Sprite(@spriteSheet)
|
||||
@addChild(@singleChildSprite)
|
||||
@addEventListener 'tick', @handleTick
|
||||
|
||||
handleTick: (e) =>
|
||||
if @lastTimeStamp
|
||||
@tick(e.timeStamp - @lastTimeStamp)
|
||||
@lastTimeStamp = e.timeStamp
|
||||
|
||||
destroy: ->
|
||||
@handleTick = undefined
|
||||
@removeAllEventListeners()
|
||||
|
||||
play: ->
|
||||
@singleChildSprite?.play()
|
||||
|
@ -22,12 +32,15 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer
|
|||
|
||||
goto: (actionName, @paused=true) ->
|
||||
@currentAnimation = actionName
|
||||
@baseMovieClip = @framerate = null
|
||||
|
||||
action = @thangType.getActions()[actionName]
|
||||
randomStart = actionName.startsWith('move')
|
||||
reg = action.positions?.registration or @thangType.get('positions')?.registration or {x:0, y:0}
|
||||
|
||||
if action.animation
|
||||
@framerate = (action.framerate ? 20) * (action.speed ? 1)
|
||||
reg = action.positions?.registration or @thangType.get('positions')?.registration or {x:0, y:0}
|
||||
|
||||
|
||||
if @singleChildSprite
|
||||
scale = SPRITE_RESOLUTION_FACTOR * (action.scale ? @thangType.get('scale') ? 1)
|
||||
@regX = -reg.x * scale
|
||||
|
@ -38,35 +51,41 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer
|
|||
@singleChildSprite.framerate = action.framerate or 20
|
||||
|
||||
if randomStart and frames = @spriteSheet.getAnimation(animationName)?.frames
|
||||
start = Math.floor(Math.random() * frames.length)
|
||||
@singleChildSprite.currentAnimationFrame = start
|
||||
@singleChildSprite.currentAnimationFrame = Math.floor(Math.random() * frames.length)
|
||||
|
||||
else
|
||||
@childSpriteContainers = []
|
||||
@baseMovieClip = @buildMovieClip(action.animation)
|
||||
@mirrorMovieClip(@baseMovieClip, @)
|
||||
@frames = action.frames
|
||||
@currentFrame = 0
|
||||
if @frames
|
||||
@frames = (parseInt(f) for f in @frames.split(','))
|
||||
|
||||
if @frames and @frames.length is 1
|
||||
@baseMovieClip.gotoAndStop(@frames[0])
|
||||
@paused = true
|
||||
@loop = action.loops isnt false
|
||||
@goesTo = action.goesTo
|
||||
@animLength = if @frames then @frames.length else @baseMovieClip.frameBounds.length
|
||||
@regX = -reg.x
|
||||
@regY = -reg.y
|
||||
@childMovieClips = []
|
||||
@baseMovieClip = @buildMovieClip(action.animation)
|
||||
@children = @baseMovieClip.children
|
||||
@frames = action.frames
|
||||
@frames = (parseInt(f) for f in @frames.split(',')) if @frames
|
||||
@animLength = if @frames then @frames.length else @baseMovieClip.frameBounds.length
|
||||
@currentFrame = if randomStart then Math.floor(Math.random() * @animLength) else 0
|
||||
@baseMovieClip.gotoAndStop(@currentFrame)
|
||||
@loop = action.loops isnt false
|
||||
@goesTo = action.goesTo
|
||||
|
||||
if randomStart
|
||||
@currentFrame = Math.floor(Math.random() * @animLength)
|
||||
if action.container
|
||||
if @singleChildSprite
|
||||
scale = SPRITE_RESOLUTION_FACTOR * (action.scale ? @thangType.get('scale') ? 1)
|
||||
@regX = -reg.x * scale
|
||||
@regY = -reg.y * scale
|
||||
animationName = @spriteSheetPrefix + actionName
|
||||
@singleChildSprite.gotoAndStop(animationName)
|
||||
|
||||
else
|
||||
@regX = -reg.x
|
||||
@regY = -reg.y
|
||||
@childMovieClips = []
|
||||
containerName = @spriteSheetPrefix + action.container
|
||||
sprite = new createjs.Sprite(@spriteSheet)
|
||||
sprite.gotoAndStop(containerName)
|
||||
@children = [sprite]
|
||||
|
||||
return
|
||||
|
||||
mirrorMovieClip: (movieClip, spriteContainer) ->
|
||||
spriteContainer.children = movieClip.children
|
||||
|
||||
buildMovieClip: (animationName, mode, startPosition, loops) ->
|
||||
raw = @thangType.get('raw')
|
||||
animData = raw.animations[animationName]
|
||||
|
@ -77,8 +96,8 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer
|
|||
_.extend locals, @buildMovieClipAnimations(animData.animations)
|
||||
|
||||
toSkip = {}
|
||||
toSkip[shape.bn] for shape in animData.shapes
|
||||
toSkip[graphic.bn] for graphic in animData.graphics
|
||||
toSkip[shape.bn] = true for shape in animData.shapes
|
||||
toSkip[graphic.bn] = true for graphic in animData.graphics
|
||||
|
||||
anim = new createjs.MovieClip()
|
||||
anim.initialize(mode ? createjs.MovieClip.INDEPENDENT, startPosition ? 0, loops ? true)
|
||||
|
@ -89,7 +108,7 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer
|
|||
for func in tweenData
|
||||
args = $.extend(true, [], (func.a))
|
||||
if @dereferenceArgs(args, locals, toSkip) is false
|
||||
console.log 'could not dereference args', args
|
||||
console.debug 'Did not dereference args:', args
|
||||
stopped = true
|
||||
break
|
||||
tween = tween[func.n](args...)
|
||||
|
@ -118,10 +137,7 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer
|
|||
animation = @buildMovieClip(localAnimation.gn, localAnimation.a...)
|
||||
animation.setTransform(localAnimation.t...)
|
||||
map[localAnimation.bn] = animation
|
||||
childSpriteContainer = new createjs.SpriteContainer(@spriteSheet)
|
||||
childSpriteContainer.movieClip = animation
|
||||
childSpriteContainer.children = animation.children
|
||||
@childSpriteContainers.push(childSpriteContainer)
|
||||
@childMovieClips.push(animation)
|
||||
return map
|
||||
|
||||
dereferenceArgs: (args, locals, toSkip) ->
|
||||
|
@ -140,8 +156,7 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer
|
|||
return args
|
||||
|
||||
tick: (delta) ->
|
||||
return unless @framerate and not @paused
|
||||
return if @singleChildSprite # this gets moved forward automatically
|
||||
return unless @baseMovieClip and @framerate and not @paused
|
||||
newFrame = @currentFrame + @framerate * delta / 1000
|
||||
|
||||
if newFrame > @animLength
|
||||
|
@ -149,7 +164,7 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer
|
|||
@gotoAndPlay(@goesTo)
|
||||
return
|
||||
else if not @loop
|
||||
@paused = false
|
||||
@paused = true
|
||||
newFrame = @animLength - 1
|
||||
@dispatchEvent('animationend')
|
||||
else
|
||||
|
@ -172,13 +187,14 @@ module.exports = class WebGLSprite extends createjs.SpriteContainer
|
|||
|
||||
@currentFrame = newFrame
|
||||
|
||||
if @childSpriteContainers
|
||||
for childSpriteContainer in @childSpriteContainers
|
||||
movieClip = childSpriteContainer.movieClip
|
||||
continue unless movieClip.parent
|
||||
index = movieClip.parent.getChildIndex(movieClip)
|
||||
movieClip.gotoAndStop(newFrame)
|
||||
movieClip.parent[index] = childSpriteContainer
|
||||
|
||||
# So, originally I thought I'd have to swap in MovieClips for parallel
|
||||
# SpriteContainers between each frame, but turns out that's not the case.
|
||||
# The WebGL rendering system treats the MovieClip like a SpriteContainer,
|
||||
# which makes things simpler for me...
|
||||
|
||||
# For some reason, though, gotoAndStop doesn't seem to advance the children
|
||||
# so I gotta do that manually.
|
||||
movieClip.gotoAndStop(newFrame) for movieClip in @childMovieClips
|
||||
|
||||
getBounds: ->
|
||||
@baseMovieClip.getBounds()
|
||||
|
|
File diff suppressed because one or more lines are too long
120
test/app/lib/surface/SpriteBoss.spec.coffee
Normal file
120
test/app/lib/surface/SpriteBoss.spec.coffee
Normal file
|
@ -0,0 +1,120 @@
|
|||
SpriteBoss = require 'lib/surface/SpriteBoss'
|
||||
Camera = require 'lib/surface/Camera'
|
||||
World = require 'lib/world/world'
|
||||
ThangType = require 'models/ThangType'
|
||||
|
||||
treeThangType = new ThangType(require 'test/app/fixtures/tree1.thang.type')
|
||||
ogreMunchkinThangType = new ThangType(require 'test/app/fixtures/ogre-munchkin-m.thang.type')
|
||||
ogreFangriderThangType = new ThangType(require 'test/app/fixtures/ogre-fangrider.thang.type')
|
||||
|
||||
describe 'SpriteBoss', ->
|
||||
spriteBoss = null
|
||||
canvas = null
|
||||
stage = null
|
||||
|
||||
# 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.
|
||||
|
||||
init = (done) ->
|
||||
return done() if spriteBoss
|
||||
t = new Date()
|
||||
canvas = $('<canvas width="800" height="600"></canvas>')
|
||||
camera = new Camera(canvas)
|
||||
|
||||
world = new World()
|
||||
world.thangs = [
|
||||
|
||||
# four cardinal ogres
|
||||
{id: 'Ogre N', spriteName: 'Ogre Munchkin M', exists: true, pos: {x:0, y:8}, action: 'move', health: 10, maxHealth: 10, rotation: -Math.PI/2, acts: true, scaleFactorX: 2 }
|
||||
{id: 'Ogre W', spriteName: 'Ogre Munchkin M', exists: true, pos: {x:-8, y:0}, action: 'move', health: 5, maxHealth: 10, rotation: 0, acts: true, scaleFactorY: 2 }
|
||||
{id: 'Ogre E', spriteName: 'Ogre Munchkin M', exists: true, pos: {x:8, y:0}, action: 'move', health: 5, maxHealth: 10, rotation: Math.PI, acts: true, alpha: 0.5, scaleFactorY: 3, scaleFactorX: 3 }
|
||||
{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 }
|
||||
|
||||
# ogres overlapping
|
||||
{id: 'Ogre 1', spriteName: 'Ogre Munchkin M', exists: true, pos: {x:-14, y:0}, action: 'die', health: 5, maxHealth: 10, rotation: 0, acts: true }
|
||||
{id: 'Ogre 2', spriteName: 'Ogre Munchkin M', exists: true, pos: {x:-13.5, y:1}, action: 'die', health: 5, maxHealth: 10, rotation: 0, acts: true }
|
||||
{id: 'Ogre 3', spriteName: 'Ogre Munchkin M', exists: true, pos: {x:-13, y:2}, action: 'die', health: 5, maxHealth: 10, rotation: 0, acts: true }
|
||||
{id: 'Ogre 4', spriteName: 'Ogre Munchkin M', exists: true, pos: {x:-12.5, y:3}, action: 'die', health: 5, maxHealth: 10, rotation: 0, acts: true }
|
||||
|
||||
{id: 'Fangrider 1', spriteName: 'Ogre Fangrider', exists: true, pos: {x:8, y:8}, action: 'move', health: 20, maxHealth: 20, rotation: 0, acts: true }
|
||||
{id: 'Tree 1', spriteName: 'Tree 1', exists: true, pos: {x:4, y:0}, action: 'idle', health: 20, maxHealth: 20, rotation: Math.PI/2, acts: true }
|
||||
]
|
||||
world.thangMap = {}
|
||||
world.thangMap[thang.id] = thang for thang in world.thangs
|
||||
|
||||
thangTypes = [treeThangType, ogreMunchkinThangType, ogreFangriderThangType]
|
||||
ogreMunchkinThangType.set('renderStrategy', 'container')
|
||||
ogreFangriderThangType.set('renderStrategy', 'container')
|
||||
treeThangType.set('renderStrategy', 'container')
|
||||
|
||||
window.stage = stage = new createjs.Stage(canvas[0])
|
||||
|
||||
options = {
|
||||
camera: camera
|
||||
surfaceLayer: stage
|
||||
surfaceTextLayer: new createjs.Container()
|
||||
world: world
|
||||
thangTypes: thangTypes
|
||||
}
|
||||
|
||||
window.spriteBoss = spriteBoss = new SpriteBoss(options)
|
||||
defaultLayer = spriteBoss.spriteLayers.Default
|
||||
defaultLayer.buildAsync = false # cause faster
|
||||
spriteBoss.update(true)
|
||||
|
||||
defaultLayer.once 'new-spritesheet', -> done()
|
||||
# showMe()
|
||||
|
||||
beforeEach (done) -> init(done)
|
||||
|
||||
showMe = ->
|
||||
canvas.css('position', 'absolute').css('index', 1000).css('background', 'white')
|
||||
$('body').append(canvas)
|
||||
|
||||
ticks = 0
|
||||
listener = {
|
||||
handleEvent: ->
|
||||
return if ticks >= 100
|
||||
ticks += 1
|
||||
console.log 'update'
|
||||
stage.update()
|
||||
}
|
||||
createjs.Ticker.addEventListener "tick", listener
|
||||
$('body').append($('<div style="position: absolute; top: 295px; left: 395px; height: 10px; width: 10px; background: red;"></div>'))
|
||||
|
||||
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')
|
||||
expect(spriteBoss.sprites['Ogre W'].imageObject.currentAnimation).toBe('move_side')
|
||||
expect(spriteBoss.sprites['Ogre S'].imageObject.currentAnimation).toBe('move_back')
|
||||
|
||||
expect(spriteBoss.sprites['Ogre E'].imageObject.scaleX).toBeLessThan(0)
|
||||
expect(spriteBoss.sprites['Ogre W'].imageObject.scaleX).toBeGreaterThan(0)
|
||||
|
||||
it 'positions sprites according to thang pos', ->
|
||||
expect(spriteBoss.sprites['Ogre N'].imageObject.x).toBe(0)
|
||||
expect(spriteBoss.sprites['Ogre N'].imageObject.y).toBeCloseTo(-60)
|
||||
expect(spriteBoss.sprites['Ogre E'].imageObject.x).toBeCloseTo(80)
|
||||
expect(spriteBoss.sprites['Ogre E'].imageObject.y).toBe(0)
|
||||
expect(spriteBoss.sprites['Ogre W'].imageObject.x).toBe(-80)
|
||||
expect(spriteBoss.sprites['Ogre W'].imageObject.y).toBeCloseTo(0)
|
||||
expect(spriteBoss.sprites['Ogre S'].imageObject.x).toBe(0)
|
||||
expect(spriteBoss.sprites['Ogre S'].imageObject.y).toBeCloseTo(60)
|
||||
|
||||
it 'scales sprites according to thang scaleFactorX and scaleFactorY', ->
|
||||
expect(spriteBoss.sprites['Ogre N'].imageObject.scaleX).toBe(spriteBoss.sprites['Ogre N'].baseScaleX * 2)
|
||||
expect(spriteBoss.sprites['Ogre W'].imageObject.scaleY).toBe(spriteBoss.sprites['Ogre N'].baseScaleY * 2)
|
||||
|
||||
it 'sets alpha based on thang alpha', ->
|
||||
expect(spriteBoss.sprites['Ogre E'].imageObject.alpha).toBe(0.5)
|
||||
|
||||
it 'orders sprites in the layer based on thang pos.y\'s', ->
|
||||
container = spriteBoss.spriteLayers.Default.spriteContainer
|
||||
l = spriteBoss.spriteLayers.Default.spriteContainer.children
|
||||
i1 = container.getChildIndex(_.find(container.children, (c) -> c.sprite.thang.id is 'Ogre 1'))
|
||||
i2 = container.getChildIndex(_.find(container.children, (c) -> c.sprite.thang.id is 'Ogre 2'))
|
||||
i3 = container.getChildIndex(_.find(container.children, (c) -> c.sprite.thang.id is 'Ogre 3'))
|
||||
i4 = container.getChildIndex(_.find(container.children, (c) -> c.sprite.thang.id is 'Ogre 4'))
|
||||
expect(i1).toBeGreaterThan(i2)
|
||||
expect(i2).toBeGreaterThan(i3)
|
||||
expect(i3).toBeGreaterThan(i4)
|
|
@ -19,9 +19,9 @@ describe 'WebGLSprite', ->
|
|||
listener = {
|
||||
handleEvent: ->
|
||||
return if ticks >= 100
|
||||
ticks += 1
|
||||
webGLSprite.tick(arguments[0].delta)
|
||||
stage.update()
|
||||
ticks += 1
|
||||
}
|
||||
createjs.Ticker.addEventListener "tick", listener
|
||||
|
||||
|
@ -57,10 +57,6 @@ describe 'WebGLSprite', ->
|
|||
webGLSprite.gotoAndStop('move_fore')
|
||||
expect(webGLSprite.paused).toBe(true)
|
||||
|
||||
it 'is paused when the action is a single frame', ->
|
||||
webGLSprite.gotoAndPlay('idle')
|
||||
expect(webGLSprite.paused).toBe(true)
|
||||
|
||||
it 'has a tick function which moves the animation forward', ->
|
||||
webGLSprite.gotoAndPlay('attack')
|
||||
webGLSprite.tick(100) # one hundred milliseconds
|
||||
|
@ -108,8 +104,21 @@ describe 'WebGLSprite', ->
|
|||
webGLSprite.tick(100) # one hundred milliseconds
|
||||
expectedFrame = webGLSprite.framerate*100/1000
|
||||
expect(webGLSprite.currentFrame).toBe(expectedFrame)
|
||||
for child in webGLSprite.childSpriteContainers
|
||||
expect(child.movieClip.currentFrame).toBe(expectedFrame)
|
||||
for movieClip in webGLSprite.childMovieClips
|
||||
expect(movieClip.currentFrame).toBe(expectedFrame)
|
||||
|
||||
it 'does not include shapes from the original animation', ->
|
||||
webGLSprite.gotoAndPlay('attack')
|
||||
webGLSprite.tick(230)
|
||||
for child in webGLSprite.children
|
||||
expect(_.isString(child)).toBe(false)
|
||||
|
||||
it 'maintains the right number of shapes', ->
|
||||
webGLSprite.gotoAndPlay('idle')
|
||||
lengths = []
|
||||
for i in _.range(10)
|
||||
webGLSprite.tick(10)
|
||||
expect(webGLSprite.children.length).toBe(20)
|
||||
|
||||
describe 'with Ogre Munchkin ThangType and renderStrategy=spriteSheet', ->
|
||||
beforeEach ->
|
||||
|
@ -138,5 +147,4 @@ describe 'WebGLSprite', ->
|
|||
|
||||
it 'has the same interface as for when the ThangType uses the container renderStrategy', ->
|
||||
webGLSprite.gotoAndPlay('move_fore')
|
||||
webGLSprite.gotoAndStop('attack')
|
||||
showMe()
|
||||
webGLSprite.gotoAndStop('attack')
|
Loading…
Reference in a new issue