mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Built the WebGLLayer which creates sprite sheets based on what CocoSprites are given to it.
This commit is contained in:
parent
5551c622fa
commit
08bdcb2cb6
10 changed files with 255 additions and 21 deletions
|
@ -11,7 +11,7 @@ module.exports = class SpriteBuilder
|
|||
|
||||
setOptions: (@options) ->
|
||||
|
||||
buildMovieClip: (animationName, movieClipArgs...) ->
|
||||
buildMovieClip: (animationName, mode, startPosition, loops, labels) ->
|
||||
animData = @animationStore[animationName]
|
||||
unless animData
|
||||
console.error 'couldn\'t find animData from', @animationStore, 'for', animationName
|
||||
|
@ -22,14 +22,10 @@ module.exports = class SpriteBuilder
|
|||
_.extend locals, @buildMovieClipAnimations(animData.animations)
|
||||
_.extend locals, @buildMovieClipGraphics(animData.graphics)
|
||||
anim = new createjs.MovieClip()
|
||||
movieClipArgs ?= []
|
||||
labels = {}
|
||||
labels[animationName] = 0
|
||||
anim.initialize(
|
||||
movieClipArgs[0] ? createjs.MovieClip.INDEPENDENT, # mode
|
||||
movieClipArgs[1] ? 0, # start position
|
||||
movieClipArgs[2] ? true, # loops
|
||||
labels)
|
||||
if not labels
|
||||
labels = {}
|
||||
labels[animationName] = 0
|
||||
anim.initialize(mode ? createjs.MovieClip.INDEPENDENT, startPosition ? 0, loops ? true, labels)
|
||||
for tweenData in animData.tweens
|
||||
tween = createjs.Tween
|
||||
for func in tweenData
|
||||
|
|
|
@ -79,13 +79,13 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
@handledDisplayEvents = {}
|
||||
@age = 0
|
||||
@stillLoading = true
|
||||
if @thangType.isFullyLoaded()
|
||||
@setUpSprite()
|
||||
else
|
||||
@thangType.setProjection null
|
||||
@thangType.fetch() unless @thangType.loading
|
||||
@listenToOnce(@thangType, 'sync', @setUpSprite)
|
||||
@setUpPlaceholder()
|
||||
# if @thangType.isFullyLoaded()
|
||||
# @setUpSprite()
|
||||
# else
|
||||
# @thangType.setProjection null
|
||||
# @thangType.fetch() unless @thangType.loading
|
||||
# @listenToOnce(@thangType, 'sync', @setUpSprite)
|
||||
# @setUpPlaceholder()
|
||||
|
||||
setUpSprite: ->
|
||||
for trigger, sounds of @thangType.get('soundTriggers') or {} when trigger isnt 'say'
|
||||
|
|
155
app/lib/surface/WebGLLayer.coffee
Normal file
155
app/lib/surface/WebGLLayer.coffee
Normal file
|
@ -0,0 +1,155 @@
|
|||
SpriteBuilder = require 'lib/sprites/SpriteBuilder'
|
||||
|
||||
module.exports = class WebGLLayer extends createjs.SpriteContainer
|
||||
|
||||
actionRenderState: null
|
||||
needToRerender: false
|
||||
toRenderBundles: null
|
||||
willRender: false
|
||||
resolutionFactor: SPRITE_RESOLUTION_FACTOR
|
||||
defaultActions: ['idle', 'die', 'move', 'move_back', 'move_side', 'move_fore', 'attack']
|
||||
|
||||
constructor: ->
|
||||
super(arguments...)
|
||||
@spriteSheetBuilder = new createjs.SpriteSheetBuilder()
|
||||
@actionRenderState = {}
|
||||
@toRenderBundles = []
|
||||
@initialize(arguments...)
|
||||
|
||||
setDefaultActions: (@defaultActions) ->
|
||||
|
||||
renderGroupingKey: (thangType, grouping, colorConfig) ->
|
||||
key = thangType.get('slug')
|
||||
if colorConfig?.team
|
||||
key += "(#{colorConfig.team.hue},#{colorConfig.team.saturation},#{colorConfig.team.lightness})"
|
||||
key += '.'+grouping if grouping
|
||||
key
|
||||
|
||||
addCocoSprite: (cocoSprite) ->
|
||||
# build the animations for it
|
||||
if cocoSprite.thangType.get('raster')
|
||||
@upsertActionToRender(cocoSprite.thangType)
|
||||
else
|
||||
for action in _.values(cocoSprite.thangType.getActions())
|
||||
continue unless action.name in @defaultActions
|
||||
@upsertActionToRender(cocoSprite.thangType, action.name, cocoSprite.options.colorConfig)
|
||||
|
||||
upsertActionToRender: (thangType, actionName, colorConfig) ->
|
||||
groupKey = @renderGroupingKey(thangType, actionName, colorConfig)
|
||||
return if @actionRenderState[groupKey] isnt undefined
|
||||
@actionRenderState[groupKey] = 'need-to-render'
|
||||
@toRenderBundles.push({thangType: thangType, actionName: actionName, colorConfig: colorConfig})
|
||||
return if @willRender
|
||||
# @willRender = _.defer => @renderNewSpriteSheet()
|
||||
|
||||
renderNewSpriteSheet: ->
|
||||
@willRender = false
|
||||
builder = new createjs.SpriteSheetBuilder()
|
||||
groups = _.groupBy(@toRenderBundles, ((bundle) -> @renderGroupingKey(bundle.thangType, '', bundle.colorConfig)), @)
|
||||
for bundleGrouping in _.values(groups)
|
||||
thangType = bundleGrouping[0].thangType
|
||||
colorConfig = bundleGrouping[0].colorConfig
|
||||
actionNames = (bundle.actionName for bundle in bundleGrouping)
|
||||
args = [thangType, colorConfig, actionNames, builder]
|
||||
if thangType.get('raw')
|
||||
if thangType.get('renderStrategy') is 'container'
|
||||
@renderContainers(args...)
|
||||
else
|
||||
@renderSpriteSheet(args...)
|
||||
else
|
||||
@renderRasterImage(thangType, builder)
|
||||
builder.build()
|
||||
|
||||
renderContainers: (thangType, colorConfig, actionNames, spriteSheetBuilder) ->
|
||||
containersToRender = {}
|
||||
for actionName in actionNames
|
||||
action = _.find(thangType.getActions(), {name: actionName})
|
||||
if action.container
|
||||
containersToRender[action.container] = true
|
||||
else if action.animation
|
||||
animationContainers = thangType.get('raw').animations[action.animation].containers
|
||||
containersToRender[container.gn] = true for container in animationContainers
|
||||
|
||||
spriteBuilder = new SpriteBuilder(thangType, {colorConfig: colorConfig})
|
||||
for containerGlobalName in _.keys(containersToRender)
|
||||
containerKey = @renderGroupingKey(thangType, containerGlobalName, colorConfig)
|
||||
container = spriteBuilder.buildContainerFromStore(containerGlobalName)
|
||||
frame = spriteSheetBuilder.addFrame(container)
|
||||
spriteSheetBuilder.addAnimation(containerKey, [frame], false)
|
||||
|
||||
|
||||
renderSpriteSheet: (thangType, colorConfig, actionNames, spriteSheetBuilder) ->
|
||||
actionObjects = _.values(thangType.getActions())
|
||||
animationActions = []
|
||||
for a in actionObjects
|
||||
continue unless a.animation
|
||||
continue unless a.name in actionNames
|
||||
animationActions.push(a)
|
||||
|
||||
spriteBuilder = new SpriteBuilder(thangType, {colorConfig: colorConfig})
|
||||
|
||||
animationGroups = _.groupBy animationActions, (action) -> action.animation
|
||||
for animationName, actions of animationGroups
|
||||
renderAll = _.any actions, (action) -> action.frames is undefined
|
||||
scale = actions[0].scale or thangType.get('scale') or 1
|
||||
mc = spriteBuilder.buildMovieClip(animationName, null, null, null, {'temp':0})
|
||||
|
||||
if renderAll
|
||||
res = spriteSheetBuilder.addMovieClip(mc, null, scale * @resolutionFactor)
|
||||
frames = spriteSheetBuilder._animations['temp'].frames
|
||||
framesMap = _.zipObject _.range(frames.length), frames
|
||||
else
|
||||
framesMap = {}
|
||||
framesToRender = _.uniq(_.flatten((a.frames.split(',') for a in actions)))
|
||||
for frame in framesToRender
|
||||
frame = parseInt(frame)
|
||||
f = _.bind(mc.gotoAndStop, mc, frame)
|
||||
framesMap[frame] = spriteSheetBuilder.addFrame(mc, null, scale * @resolutionFactor, f)
|
||||
|
||||
for action in actions
|
||||
name = @renderGroupingKey(thangType, action.name, colorConfig)
|
||||
|
||||
if action.frames
|
||||
frames = (framesMap[parseInt(frame)] for frame in action.frames.split(','))
|
||||
else
|
||||
frames = _.values(framesMap).sort()
|
||||
|
||||
next = true
|
||||
next = action.goesTo if action.goesTo
|
||||
next = false if action.loops is false
|
||||
|
||||
spriteSheetBuilder.addAnimation(name, frames, next)
|
||||
|
||||
containerActions = []
|
||||
for a in actionObjects
|
||||
continue unless a.container
|
||||
continue unless a.name in actionNames
|
||||
containerActions.push(a)
|
||||
|
||||
containerGroups = _.groupBy containerActions, (action) -> action.container
|
||||
for containerName, actions of containerGroups
|
||||
container = spriteBuilder.buildContainerFromStore(containerName)
|
||||
scale = actions[0].scale or thangType.get('scale') or 1
|
||||
frame = spriteSheetBuilder.addFrame(container, null, scale * @resolutionFactor)
|
||||
for action in actions
|
||||
name = @renderGroupingKey(thangType, action.name, colorConfig)
|
||||
spriteSheetBuilder.addAnimation(name, [frame], false)
|
||||
|
||||
renderRasterImage: (thangType, spriteSheetBuilder) ->
|
||||
unless thangType.rasterImage
|
||||
console.error("Cannot render the WebGLLayer SpriteSheet until the raster image for <#{thangType.get('name')}> is loaded.")
|
||||
|
||||
bm = new createjs.Bitmap(thangType.rasterImage[0])
|
||||
scale = thangType.get('scale') or 1
|
||||
frame = spriteSheetBuilder.addFrame(bm, null, scale)
|
||||
spriteSheetBuilder.addAnimation(@renderGroupingKey(thangType), [frame], false)
|
||||
|
||||
# renderForSprite: (cocoSprite) ->
|
||||
# rawData = cocoSprite.thangType.get('raw')
|
||||
# groupKey = @renderGroupingKey(cocoSprite.thangType, cocoSprite.options.colorConfig)
|
||||
# spriteBuilder = new SpriteBuilder(cocoSprite.thangType, cocoSprite.options)
|
||||
# for animation in raw.animations ? []
|
||||
# for shape in shapes
|
||||
# shape = @spriteBuilder.buildShapeFromStore(shape.gn)
|
||||
# key = groupKey = ':' + shape.gn
|
||||
# frame = @spriteBuilder.addFrame(groupKey)
|
0
app/lib/surface/WebGLSprite.coffee
Normal file
0
app/lib/surface/WebGLSprite.coffee
Normal file
|
@ -33,7 +33,12 @@ module.exports = class ThangType extends CocoModel
|
|||
isFullyLoaded: ->
|
||||
# TODO: Come up with a better way to identify when the model doesn't have everything needed to build the sprite. ie when it's a projection without all the required data.
|
||||
return @get('actions') or @get('raster') # needs one of these two things
|
||||
|
||||
|
||||
loadRasterImage: ->
|
||||
return unless raster = @get('raster')
|
||||
@rasterImage = $("<img src='/file/#{raster}' />")
|
||||
@rasterImage.on('load', => @trigger('raster-image-loaded'))
|
||||
|
||||
getActions: ->
|
||||
return {} unless @isFullyLoaded()
|
||||
return @actions or @buildActions()
|
||||
|
|
1
test/app/fixtures/leather-boots.thang.type.js
Normal file
1
test/app/fixtures/leather-boots.thang.type.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = {"_id":"53f6a5fdd822c23505b91a92","index":true,"slug":"leather-boots","kind":"Item","name":"Leather Boots","creator":"51538fdb812dd9af02000001","original":"53e2384453457600003e3f07","watchers":["512ef4805a67a8c507000001"],"__v":0,"components":[{"original":"524b85837fc0f6d519000020","majorVersion":0},{"original":"524b7b857fc0f6d519000012","majorVersion":0},{"original":"524b4150ff92f1f4f8000024","majorVersion":0},{"original":"53e12043b82921000051cdf9","majorVersion":0,"config":{"ownerID":"Tharin","slots":["feet"],"programmableProperties":["moveXY"],"moreProgrammableProperties":[],"extraHUDProperties":["maxSpeed"],"stats":{"maxSpeed":{"factor":1}}}},{"original":"524b7b8c7fc0f6d519000013","majorVersion":0,"config":{"locomotionType":"running","maxSpeed":5,"maxAcceleration":100}},{"original":"524b75ad7fc0f6d519000001","majorVersion":0,"config":{"pos":{"x":39.08,"y":20.72,"z":0.5},"width":1,"height":1,"depth":1,"shape":"ellipsoid"}},{"original":"524b7b7c7fc0f6d519000011","majorVersion":0}],"commitMessage":"added portrait","parent":"53eb988c1a100989a40ce465","raster":"db/thang.type/53e2384453457600003e3f07/boots.png","rasterIcon":"db/thang.type/53e2384453457600003e3f07/boots_2.jpg","created":"2014-08-22T02:07:57.209Z","version":{"isLatestMinor":true,"isLatestMajor":true,"minor":4,"major":0}}
|
1
test/app/fixtures/ogre-munchkin-m.thang.type.js
Normal file
1
test/app/fixtures/ogre-munchkin-m.thang.type.js
Normal file
File diff suppressed because one or more lines are too long
1
test/app/fixtures/tree1.thang.type.js
Normal file
1
test/app/fixtures/tree1.thang.type.js
Normal file
File diff suppressed because one or more lines are too long
72
test/app/lib/surface/WebGLLayer.spec.coffee
Normal file
72
test/app/lib/surface/WebGLLayer.spec.coffee
Normal file
|
@ -0,0 +1,72 @@
|
|||
WebGLLayer = require 'lib/surface/WebGLLayer'
|
||||
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 'WebGLLayer', ->
|
||||
it 'creates containers for animated actions if set to renderStrategy=container', ->
|
||||
layer = new WebGLLayer()
|
||||
ogreMunchkinThangType.set('renderStrategy', 'container')
|
||||
colorConfig = {team: {hue: 0, saturation: 1, lightness: 0.5}}
|
||||
sprite = new CocoSprite(ogreMunchkinThangType, {colorConfig: colorConfig})
|
||||
layer.addCocoSprite(sprite)
|
||||
sheet = layer.renderNewSpriteSheet()
|
||||
key = layer.renderGroupingKey(ogreMunchkinThangType, 'head', colorConfig)
|
||||
expect(key in sheet.getAnimations()).toBe(true)
|
||||
|
||||
it 'creates the container for static actions if set to renderStrategy=container', ->
|
||||
layer = new WebGLLayer()
|
||||
treeThangType.set('renderStrategy', 'container')
|
||||
sprite = new CocoSprite(treeThangType)
|
||||
layer.addCocoSprite(sprite)
|
||||
sheet = layer.renderNewSpriteSheet()
|
||||
key = layer.renderGroupingKey(treeThangType, 'Tree_4')
|
||||
expect(key in sheet.getAnimations()).toBe(true)
|
||||
|
||||
it 'creates animations for animated actions if set to renderStrategy=spriteSheet', ->
|
||||
layer = new WebGLLayer()
|
||||
ogreMunchkinThangType.set('renderStrategy', 'spriteSheet')
|
||||
colorConfig = {team: {hue: 0, saturation: 1, lightness: 0.5}}
|
||||
sprite = new CocoSprite(ogreMunchkinThangType, {colorConfig: colorConfig})
|
||||
layer.addCocoSprite(sprite)
|
||||
sheet = layer.renderNewSpriteSheet()
|
||||
key = layer.renderGroupingKey(ogreMunchkinThangType, 'idle', colorConfig)
|
||||
expect(key in sheet.getAnimations()).toBe(true)
|
||||
|
||||
it 'creates animations for static actions if set to renderStrategy=spriteSheet', ->
|
||||
layer = new WebGLLayer()
|
||||
treeThangType.set('renderStrategy', 'spriteSheet')
|
||||
sprite = new CocoSprite(treeThangType)
|
||||
layer.addCocoSprite(sprite)
|
||||
sheet = layer.renderNewSpriteSheet()
|
||||
key = layer.renderGroupingKey(treeThangType, 'idle')
|
||||
expect(key in sheet.getAnimations()).toBe(true)
|
||||
|
||||
it 'only renders frames used by actions when renderStrategy=spriteSheet', ->
|
||||
layer = new WebGLLayer()
|
||||
layer.setDefaultActions(['idle']) # uses the move side animation
|
||||
ogreMunchkinThangType.set('renderStrategy', 'spriteSheet')
|
||||
colorConfig = {team: {hue: 0, saturation: 1, lightness: 0.5}}
|
||||
sprite = new CocoSprite(ogreMunchkinThangType, {colorConfig: colorConfig})
|
||||
layer.addCocoSprite(sprite)
|
||||
sheet = layer.renderNewSpriteSheet()
|
||||
key = layer.renderGroupingKey(ogreMunchkinThangType, 'idle', colorConfig)
|
||||
animations = sheet.getAnimations()
|
||||
expect(animations.length).toBe(1)
|
||||
expect(animations[0]).toBe(key)
|
||||
expect(sheet.getNumFrames()).toBe(1)
|
||||
|
||||
it 'renders a raster image onto a sheet', (done) ->
|
||||
bootsThangType = new ThangType(require 'test/app/fixtures/leather-boots.thang.type')
|
||||
bootsThangType.loadRasterImage()
|
||||
bootsThangType.once('raster-image-loaded', ->
|
||||
layer = new WebGLLayer()
|
||||
sprite = new CocoSprite(bootsThangType)
|
||||
layer.addCocoSprite(sprite)
|
||||
sheet = layer.renderNewSpriteSheet()
|
||||
key = layer.renderGroupingKey(bootsThangType)
|
||||
expect(key in sheet.getAnimations()).toBe(true)
|
||||
done()
|
||||
#$('body').attr('class', '').empty().css('background', 'white').append($(sheet._images))
|
||||
)
|
|
@ -443,13 +443,17 @@ class WebGLDemoView extends RootView
|
|||
spriteContainers = []
|
||||
i = 0
|
||||
|
||||
class SpriteContainerChildClass extends createjs.SpriteContainer
|
||||
constructor: (spriteSheet) ->
|
||||
@initialize(spriteSheet)
|
||||
|
||||
while i < 100
|
||||
beStatic = false
|
||||
# beStatic = i % 2
|
||||
# beStatic = true
|
||||
|
||||
librarian = new librarianLib.Librarian_SideWalk_JSCC()
|
||||
c = new createjs.SpriteContainer(sheet)
|
||||
c = new SpriteContainerChildClass(sheet)
|
||||
c.x = (i%10) * 95
|
||||
c.y = i * 6
|
||||
c.scaleX = 1
|
||||
|
@ -514,11 +518,10 @@ class WebGLDemoView extends RootView
|
|||
# @testAnimateManyRasteredWaterfalls()
|
||||
# @testManualMovieClipUpdating()
|
||||
# @testManyWaterfallsWithManualAnimation()
|
||||
# @testLibrarianHorde()
|
||||
@testGiantCanvas()
|
||||
@testLibrarianHorde()
|
||||
|
||||
module.exports = ->
|
||||
v = new WebGLDemoView()
|
||||
v.render()
|
||||
window.v = v
|
||||
v
|
||||
v
|
Loading…
Reference in a new issue