Built most of the WebGLSprite.

This commit is contained in:
Scott Erickson 2014-09-15 13:53:20 -07:00
parent c96f5d7bcf
commit a633f6f82f
4 changed files with 221 additions and 2 deletions

View file

@ -77,7 +77,7 @@ module.exports = class SpriteBuilder
buildMovieClipAnimations: (localAnimations) -> buildMovieClipAnimations: (localAnimations) ->
map = {} map = {}
for localAnimation in localAnimations for localAnimation in localAnimations
animation = @buildMovieClip(localAnimation.gn, localAnimation.a) animation = @buildMovieClip(localAnimation.gn, localAnimation.a...)
animation.setTransform(localAnimation.t...) animation.setTransform(localAnimation.t...)
map[localAnimation.bn] = animation map[localAnimation.bn] = animation
map map

View file

@ -11,7 +11,6 @@ module.exports = class WebGLLayer extends createjs.SpriteContainer
constructor: -> constructor: ->
super(arguments...) super(arguments...)
@spriteSheetBuilder = new createjs.SpriteSheetBuilder()
@actionRenderState = {} @actionRenderState = {}
@toRenderBundles = [] @toRenderBundles = []
@initialize(arguments...) @initialize(arguments...)

View file

@ -0,0 +1,141 @@
SpriteBuilder = require 'lib/sprites/SpriteBuilder'
module.exports = class WebGLSprite extends createjs.SpriteContainer
constructor: (@spriteSheet, @thangType, @spriteSheetPrefix) ->
@initialize(@spriteSheet)
gotoAndPlay: (actionName) -> @goto(actionName, false)
gotoAndStop: (actionName) -> @goto(actionName, true)
goto: (actionName, @paused=true) ->
@currentAnimation = actionName
action = @thangType.getActions()[actionName]
if action.animation
@baseMovieClip = @buildMovieClip(action.animation)
@mirrorMovieClip(@baseMovieClip, @)
@framerate = (action.framerate ? 20) * (action.speed ? 1)
@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
return
mirrorMovieClip: (movieClip, spriteContainer) ->
movieClips = []
spriteContainer.children = movieClip.children
for child, index in spriteContainer.children
if child instanceof createjs.MovieClip
movieClips.push(child)
childMovieClip = child
childSpriteContainer = new createjs.SpriteContainer(@spriteSheet)
spriteContainer.children[index] = childSpriteContainer
movieClips = movieClips.concat(@mirrorMovieClip(childMovieClip, childSpriteContainer))
return movieClips
buildMovieClip: (animationName, mode, startPosition, loops) ->
raw = @thangType.get('raw')
animData = raw.animations[animationName]
movieClip = new createjs.MovieClip()
locals = {}
_.extend locals, @buildMovieClipContainers(animData.containers)
_.extend locals, @buildMovieClipAnimations(animData.animations)
anim = new createjs.MovieClip()
anim.initialize(mode ? createjs.MovieClip.INDEPENDENT, startPosition ? 0, loops ? true)
for tweenData in animData.tweens
stopped = false
tween = createjs.Tween
for func in tweenData
args = $.extend(true, [], (func.a))
args = @dereferenceArgs(args, locals)
if args is false
console.log 'could not dereference args', args
stopped = true
break
tween = tween[func.n](args...)
continue if stopped
anim.timeline.addTween(tween)
anim.nominalBounds = new createjs.Rectangle(animData.bounds...)
if animData.frameBounds
anim.frameBounds = (new createjs.Rectangle(bounds...) for bounds in animData.frameBounds)
return anim
buildMovieClipContainers: (localContainers) ->
map = {}
for localContainer in localContainers
container = new createjs.Sprite(@spriteSheet)
container.gotoAndStop(@spriteSheetPrefix + localContainer.gn)
container.setTransform(localContainer.t...)
container._off = localContainer.o if localContainer.o?
container.alpha = localContainer.al if localContainer.al?
map[localContainer.bn] = container
return map
buildMovieClipAnimations: (localAnimations) ->
map = {}
for localAnimation in localAnimations
animation = @buildMovieClip(localAnimation.gn, localAnimation.a...)
animation.setTransform(localAnimation.t...)
map[localAnimation.bn] = animation
return map
dereferenceArgs: (args, locals) ->
for key, val of args
if locals[val]
args[key] = locals[val]
else if val is null
args[key] = {}
else if _.isString(val) and val.indexOf('createjs.') is 0
args[key] = eval(val) # TODO: Security risk
else if _.isObject(val) or _.isArray(val)
res = @dereferenceArgs(val, locals)
return res if res is false
else if _.isString(val)
return false
return args
tick: (delta) ->
return unless @framerate and not @paused
newFrame = @currentFrame + @framerate * delta / 1000
if newFrame > @animLength
if @goesTo
@gotoAndPlay(@goesTo)
return
else if not @loop
@paused = false
newFrame = @animLength - 1
@dispatchEvent('animationend')
else
newFrame = newFrame % @animLength
if @frames
prevFrame = Math.floor(newFrame)
nextFrame = Math.ceil(newFrame)
if prevFrame is nextFrame
@baseMovieClip.gotoAndStop(@frames[newFrame])
else if nextFrame is @frames.length
@baseMovieClip.gotoAndStop(@frames[prevFrame])
else
# interpolate between frames
pct = newFrame % 1
newFrameIndex = @frames[prevFrame] + (pct * (@frames[nextFrame] - @frames[prevFrame]))
@baseMovieClip.gotoAndStop(newFrameIndex)
else
@baseMovieClip.gotoAndStop(newFrame)
@currentFrame = newFrame
getBounds: ->
@baseMovieClip.getBounds()

View file

@ -0,0 +1,79 @@
WebGLLayer = require 'lib/surface/WebGLLayer'
WebGLSprite = require 'lib/surface/WebGLSprite'
CocoSprite = require 'lib/surface/CocoSprite'
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')
describe 'WebGLSprite', ->
webGLSprite = null
showMe = ->
canvas = $('<canvas width="600" height="400"></canvas>').css('position', 'absolute').css('index', 1000).css('background', 'white')
$('body').append(canvas)
stage = new createjs.SpriteStage(canvas[0])
stage.addChild(webGLSprite)
ticks = 0
listener = {
handleEvent: ->
# return if ticks >= 100
webGLSprite.tick(arguments[0].delta)
stage.update()
ticks += 1
}
createjs.Ticker.addEventListener "tick", listener
beforeEach ->
layer = new WebGLLayer()
ogreMunchkinThangType.markToRevert()
ogreMunchkinThangType.set('renderStrategy', 'container')
actions = ogreMunchkinThangType.getActions()
# couple extra actions for doing some tests
actions.littledance = {animation:'enemy_small_move_side',framerate:1, frames:'0,6,2,6,2,8,0'}
actions.onestep = {animation:'enemy_small_move_side', loops: false}
colorConfig = {team: {hue: 0, saturation: 1, lightness: 0.5}}
sprite = new CocoSprite(ogreMunchkinThangType, {colorConfig: colorConfig})
layer.addCocoSprite(sprite)
sheet = layer.renderNewSpriteSheet()
prefix = layer.renderGroupingKey(ogreMunchkinThangType, null, colorConfig) + '.'
window.webGLSprite = webGLSprite = new WebGLSprite(sheet, ogreMunchkinThangType, prefix)
afterEach ->
ogreMunchkinThangType.revert()
it 'has gotoAndPlay, gotoAndStop, and paused like a MovieClip or Sprite', ->
webGLSprite.gotoAndPlay('move_fore')
expect(webGLSprite.baseMovieClip).toBeDefined()
expect(webGLSprite.paused).toBe(false)
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('move_fore')
webGLSprite.tick(100) # one hundred milliseconds
expect(webGLSprite.baseMovieClip.currentFrame).toBe(webGLSprite.framerate*100/1000)
it 'will interpolate between frames of a custom frame set', ->
webGLSprite.gotoAndPlay('littledance')
webGLSprite.tick(1000)
expect(webGLSprite.baseMovieClip.currentFrame).toBe(6)
webGLSprite.tick(1000)
expect(webGLSprite.baseMovieClip.currentFrame).toBe(2)
webGLSprite.tick(500)
expect(webGLSprite.baseMovieClip.currentFrame).toBe(4)
webGLSprite.tick(500)
expect(webGLSprite.baseMovieClip.currentFrame).toBe(6)
it 'emits animationend for animations where loops is false and there is no goesTo', ->
fired = false
webGLSprite.gotoAndPlay('onestep')
webGLSprite.on('animationend', -> fired = true)
webGLSprite.tick(1000)
expect(fired).toBe(true)