codecombat/app/lib/sprites/SpriteExporter.coffee
Scott Erickson 919e0605e9 Add spriteSheets to ThangType, export modal to Thang Editor
Units can be exported as rastered sprite sheets. This is the first part of the project,
the second part will be having the game use them.
2015-10-12 16:47:48 -07:00

98 lines
No EOL
4 KiB
CoffeeScript

SpriteBuilder = require('./SpriteBuilder')
ThangType = require('models/ThangType')
CocoClass = require('core/CocoClass')
class SpriteExporter extends CocoClass
'''
To be used by the ThangTypeEditView to export ThangTypes to single sprite sheets which can be uploaded to
GridFS and used in gameplay, avoiding rendering vector images.
Code has been copied and reworked and simplified from LayerAdapter. Some shared code has been refactored into
ThangType, but more work could be done to rethink and reorganize Sprite rendering.
'''
constructor: (thangType, options) ->
@thangType = thangType
options ?= {}
@colorConfig = options.colorConfig or {}
@resolutionFactor = options.resolutionFactor or 1
@actionNames = options.actionNames or (action.name for action in @thangType.getDefaultActions())
super()
build: (renderType) ->
spriteSheetBuilder = new createjs.SpriteSheetBuilder()
if (renderType or @thangType.get('spriteType') or 'segmented') is 'segmented'
@renderSegmentedThangType(spriteSheetBuilder)
else
@renderSingularThangType(spriteSheetBuilder)
try
spriteSheetBuilder.buildAsync()
catch e
@resolutionFactor *= 0.9
return @build()
spriteSheetBuilder.on 'complete', @onBuildSpriteSheetComplete, @, true, spriteSheetBuilder
@asyncBuilder = spriteSheetBuilder
renderSegmentedThangType: (spriteSheetBuilder) ->
containersToRender = @thangType.getContainersForActions(@actionNames)
spriteBuilder = new SpriteBuilder(@thangType, {colorConfig: @colorConfig})
for containerGlobalName in containersToRender
container = spriteBuilder.buildContainerFromStore(containerGlobalName)
frame = spriteSheetBuilder.addFrame(container, null, @resolutionFactor * (@thangType.get('scale') or 1))
spriteSheetBuilder.addAnimation(containerGlobalName, [frame], false)
renderSingularThangType: (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
scale = actions[0].scale or @thangType.get('scale') or 1
mc = spriteBuilder.buildMovieClip(animationName, null, null, null, {'temp':0})
spriteSheetBuilder.addMovieClip(mc, null, scale * @resolutionFactor)
frames = spriteSheetBuilder._animations['temp'].frames
framesMap = _.zipObject _.range(frames.length), frames
for action in actions
if action.frames
frames = (framesMap[parseInt(frame)] for frame in action.frames.split(','))
else
frames = _.sortBy(_.values(framesMap))
next = @nextForAction(action)
spriteSheetBuilder.addAnimation(action.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
spriteSheetBuilder.addAnimation(action.name, [frame], false)
onBuildSpriteSheetComplete: (e, builder) ->
if builder.spriteSheet._images.length > 1
total = 0
# get a rough estimate of how much smaller the spritesheet needs to be
for image, index in builder.spriteSheet._images
total += image.height / builder.maxHeight
@resolutionFactor /= (Math.max(1.1, Math.sqrt(total)))
@_renderNewSpriteSheet(e.async)
return
@trigger 'build', { spriteSheet: builder.spriteSheet }
module.exports = SpriteExporter