mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-07 10:34:46 -04:00
Several performance improvements based on profiling.
This commit is contained in:
parent
e5e146c3c1
commit
bd3c059c2a
7 changed files with 65 additions and 45 deletions
app
lib/surface
models
views/play/level
|
@ -35,12 +35,13 @@ module.exports = class Layer extends createjs.Container
|
|||
@transformStyle = options.transform ? Layer.TRANSFORM_CHILD
|
||||
@camera = options.camera
|
||||
console.error @toString(), "needs a camera." unless @camera
|
||||
@updateLayerOrder = _.throttle @updateLayerOrder, 1 # don't call multiple times in one frame
|
||||
@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
|
||||
|
||||
destroy: ->
|
||||
child.destroy?() for child in @children
|
||||
Backbone.Mediator.unsubscribe(channel, @[func], @) for channel, func of @subscriptions
|
||||
delete @updateLayerOrder
|
||||
|
||||
toString: -> "<Layer #{@layerPriority}: #{@name}>"
|
||||
|
||||
|
@ -60,21 +61,30 @@ module.exports = class Layer extends createjs.Container
|
|||
|
||||
updateLayerOrder: =>
|
||||
#console.log @, @toString(), "sorting children", _.clone @children if @name is 'Default'
|
||||
@sortChildren (a, b) ->
|
||||
alp = a.layerPriority ? 0
|
||||
blp = b.layerPriority ? 0
|
||||
return alp - blp if alp isnt blp
|
||||
# TODO: remove this z stuff
|
||||
az = if a.z then a.z else 1000
|
||||
bz = if b.z then b.z else 1000
|
||||
aThang = a.sprite?.thang
|
||||
bThang = b.sprite?.thang
|
||||
az -= 1 if aThang?.health < 0
|
||||
bz -= 1 if bThang?.health < 0
|
||||
if az == bz
|
||||
return 0 unless aThang?.pos and bThang?.pos
|
||||
return (bThang.pos.y - aThang.pos.y) or (bThang.pos.x - aThang.pos.x)
|
||||
return az - bz
|
||||
@sortChildren @layerOrderComparator
|
||||
|
||||
layerOrderComparator: (a, b) ->
|
||||
# Optimize
|
||||
alp = a.layerPriority or 0
|
||||
blp = b.layerPriority or 0
|
||||
return alp - blp if alp isnt blp
|
||||
# TODO: remove this z stuff
|
||||
az = a.z or 1000
|
||||
bz = b.z or 1000
|
||||
if aSprite = a.sprite
|
||||
if aThang = aSprite.thang
|
||||
aPos = aThang.pos
|
||||
if aThang.health < 0
|
||||
--az
|
||||
if bSprite = b.sprite
|
||||
if bThang = bSprite.thang
|
||||
bPos = bThang.pos
|
||||
if bThang.health < 0
|
||||
--bz
|
||||
if az is bz
|
||||
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
|
||||
|
|
|
@ -34,17 +34,18 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
@world = options.world
|
||||
@options.thangTypes ?= []
|
||||
@sprites = {}
|
||||
@spriteArray = [] # Mirror @sprites, but faster for when we just need to iterate
|
||||
@selfWizardSprite = null
|
||||
@createLayers()
|
||||
@spriteSheetCache = {}
|
||||
|
||||
destroy: ->
|
||||
@removeSprite sprite for thangID, sprite of @sprites
|
||||
@removeSprite sprite for sprite in @spriteArray
|
||||
@targetMark?.destroy()
|
||||
@selectionMark?.destroy()
|
||||
super()
|
||||
|
||||
toString: -> "<SpriteBoss: #{@sprites.length} sprites>"
|
||||
toString: -> "<SpriteBoss: #{@spriteArray.length} sprites>"
|
||||
|
||||
thangTypeFor: (type) ->
|
||||
_.find @options.thangTypes, (m) -> m.get('original') is type or m.get('name') is type
|
||||
|
@ -77,6 +78,7 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
id ?= sprite.thang.id
|
||||
console.error "Sprite collision! Already have:", id if @sprites[id]
|
||||
@sprites[id] = sprite
|
||||
@spriteArray.push sprite
|
||||
layer ?= @spriteLayers["Obstacle"] if sprite.thang?.spriteName.search(/(dungeon|indoor).wall/i) isnt -1
|
||||
layer ?= @layerForChild sprite.displayObject, sprite
|
||||
layer.addChild sprite.displayObject
|
||||
|
@ -117,7 +119,6 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
else
|
||||
sprite.targetPos = if opponent.team is 'ogres' then {x:52, y: 28} else {x: 20, y:28}
|
||||
|
||||
|
||||
createWizardSprite: (options) ->
|
||||
sprite = new WizardSprite @thangTypeFor("Wizard"), @createSpriteOptions(options)
|
||||
@addSprite sprite, sprite.thang.id, @spriteLayers["Floating"]
|
||||
|
@ -138,7 +139,7 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
onSetDebug: (e) ->
|
||||
return if e.debug is @debug
|
||||
@debug = e.debug
|
||||
sprite.setDebug @debug for thangID, sprite of @sprites
|
||||
sprite.setDebug @debug for sprite in @spriteArray
|
||||
|
||||
onHighlightSprites: (e) ->
|
||||
highlightedIDs = e.thangIDs or []
|
||||
|
@ -159,15 +160,16 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
sprite.displayObject.parent.removeChild sprite.displayObject
|
||||
thang = sprite.thang
|
||||
delete @sprites[sprite.thang.id]
|
||||
@spriteArray.splice @spriteArray.indexOf(sprite), 1
|
||||
sprite.destroy()
|
||||
sprite.thang = thang # Keep around so that we know which thang the destroyed thang was for
|
||||
|
||||
updateSounds: ->
|
||||
sprite.playSounds() for thangID, sprite of @sprites # hmm; doesn't work for sprites which we didn't add yet in adjustSpriteExistence
|
||||
sprite.playSounds() for sprite in @spriteArray # hmm; doesn't work for sprites which we didn't add yet in adjustSpriteExistence
|
||||
|
||||
update: (frameChanged) ->
|
||||
@adjustSpriteExistence() if frameChanged
|
||||
sprite.update frameChanged for thangID, sprite of @sprites
|
||||
sprite.update frameChanged for sprite in @spriteArray
|
||||
@updateSelection()
|
||||
@spriteLayers["Default"].updateLayerOrder()
|
||||
@cache()
|
||||
|
@ -197,7 +199,7 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
|
||||
cache: (update=false) ->
|
||||
return if @cached and not update
|
||||
wallSprites = (sprite for thangID, sprite of @sprites when sprite.thangType?.get('name').search(/(dungeon|indoor).wall/i) isnt -1)
|
||||
wallSprites = (sprite for sprite in @spriteArray when sprite.thangType?.get('name').search(/(dungeon|indoor).wall/i) isnt -1)
|
||||
walls = (sprite.thang for sprite in wallSprites)
|
||||
@world.calculateBounds()
|
||||
wallGrid = new Grid walls, @world.size()...
|
||||
|
@ -222,12 +224,12 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
onCastSpells: -> @stop()
|
||||
|
||||
play: ->
|
||||
sprite.imageObject.play() for thangID, sprite of @sprites
|
||||
sprite.imageObject.play() for sprite in @spriteArray
|
||||
@selectionMark?.play()
|
||||
@targetMark?.play()
|
||||
|
||||
stop: ->
|
||||
sprite.imageObject.stop() for thangID, sprite of @sprites
|
||||
sprite.imageObject.stop() for sprite in @spriteArray
|
||||
@selectionMark?.stop()
|
||||
@targetMark?.stop()
|
||||
|
||||
|
|
|
@ -221,8 +221,8 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@currentFrame = tempFrame
|
||||
frame = @world.getFrame(@getCurrentFrame())
|
||||
frame.restoreState()
|
||||
for thangID, sprite of @spriteBoss.sprites
|
||||
sprite.playSounds false, Math.max(0.05, Math.min(1, 1 / @scrubbingPlaybackSpeed))
|
||||
volume = Math.max(0.05, Math.min(1, 1 / @scrubbingPlaybackSpeed))
|
||||
sprite.playSounds false, volume for sprite in @spriteBoss.spriteArray
|
||||
tempFrame += if rising then 1 else -1
|
||||
@currentFrame = actualCurrentFrame
|
||||
|
||||
|
|
|
@ -8,14 +8,14 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
|
||||
@models = {}
|
||||
@collections = {}
|
||||
|
||||
|
||||
# Since the supermodel has undergone some changes into being a loader and a cache interface,
|
||||
# it's a bit wonky to use. The next couple functions are meant to cover the majority of
|
||||
# use cases across the site. If they are used, the view will automatically handle errors,
|
||||
# retries, progress, and filling the cache. Note that the resource it passes back will not
|
||||
# necessarily have the same model or collection that was passed in, if it was fetched from
|
||||
# the cache.
|
||||
|
||||
|
||||
loadModel: (model, name, fetchOptions, value=1) ->
|
||||
cachedModel = @getModelByURL(model.getURL())
|
||||
if cachedModel
|
||||
|
@ -28,12 +28,12 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
res = @addModelResource(cachedModel, name, fetchOptions, value)
|
||||
res.markLoading()
|
||||
return res
|
||||
|
||||
|
||||
else
|
||||
@registerModel(model)
|
||||
console.debug 'Registering model', model.getURL()
|
||||
return @addModelResource(model, name, fetchOptions, value).load()
|
||||
|
||||
|
||||
loadCollection: (collection, name, fetchOptions, value=1) ->
|
||||
url = collection.getURL()
|
||||
if cachedCollection = @collections[url]
|
||||
|
@ -46,7 +46,7 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
res = @addModelResource(cachedCollection, name, fetchOptions, value)
|
||||
res.markLoading()
|
||||
return res
|
||||
|
||||
|
||||
else
|
||||
@addCollection collection
|
||||
@listenToOnce collection, 'sync', (c) ->
|
||||
|
@ -56,7 +56,7 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
|
||||
# replace or overwrite
|
||||
shouldSaveBackups: (model) -> false
|
||||
|
||||
|
||||
# Caching logic
|
||||
|
||||
getModel: (ModelClass_or_url, id) ->
|
||||
|
@ -102,7 +102,7 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
else
|
||||
@registerModel(model)
|
||||
collection
|
||||
|
||||
|
||||
# Tracking resources being loaded for this supermodel
|
||||
|
||||
finished: ->
|
||||
|
@ -148,7 +148,7 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
@trigger('failed', source)
|
||||
|
||||
updateProgress: =>
|
||||
# Because this is _.defer'd, this might end up getting called after
|
||||
# Because this is _.defer'd, this might end up getting called after
|
||||
# a bunch of things load all at once.
|
||||
# So make sure we only emit events if @progress has changed.
|
||||
newProg = if @denom then @num / @denom else 1
|
||||
|
@ -161,8 +161,8 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
|
||||
getResource: (rid) ->
|
||||
return @resources[rid]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Resource extends Backbone.Model
|
||||
constructor: (name, value=1) ->
|
||||
|
@ -185,11 +185,11 @@ class Resource extends Backbone.Model
|
|||
@trigger('failed', {resource: @})
|
||||
@isLoaded = @isLoading = false
|
||||
@isFailed = true
|
||||
|
||||
|
||||
markLoading: ->
|
||||
@isLoaded = @isFailed = false
|
||||
@isLoading = true
|
||||
|
||||
|
||||
load: -> @
|
||||
|
||||
|
||||
|
|
|
@ -306,10 +306,11 @@ module.exports = class HUDView extends View
|
|||
for actionName, action of @thang.actions
|
||||
@updateActionElement(actionName, @timespans[actionName], @thang.action is actionName)
|
||||
tableContainer = @$el.find('.table-container')
|
||||
timelineWidth = tableContainer.find('tr:not(.secret) .action-timeline').width()
|
||||
right = (1 - (@timeProgress ? 0)) * timelineWidth
|
||||
arrow = tableContainer.find('.progress-arrow')
|
||||
arrow.css 'right', right - arrow.width() / 2
|
||||
@timelineWidth ||= tableContainer.find('tr:not(.secret) .action-timeline').width()
|
||||
@actionArrowWidth ||= arrow.width()
|
||||
right = (1 - (@timeProgress ? 0)) * @timelineWidth
|
||||
arrow.css 'right', right - @actionArrowWidth / 2
|
||||
tableContainer.find('.progress-line').css 'right', right
|
||||
|
||||
buildActionTimespans: ->
|
||||
|
|
|
@ -238,7 +238,7 @@ module.exports = class PlaybackView extends View
|
|||
@currentTime = e.frame / e.world.frameRate
|
||||
# Game will sometimes stop at 29.97, but with only one digit, this is unnecesary.
|
||||
# @currentTime = @totalTime if Math.abs(@totalTime - @currentTime) < 0.04
|
||||
@updatePopupContent()
|
||||
@updatePopupContent() if @timePopup?.shown
|
||||
|
||||
@updateProgress(e.progress)
|
||||
@updatePlayButton(e.progress)
|
||||
|
|
|
@ -156,9 +156,16 @@ module.exports = class ThangListEntryView extends View
|
|||
@$el.toggleClass('disabled', not enabled)
|
||||
|
||||
onFrameChanged: (e) ->
|
||||
# Optimize
|
||||
return unless currentThang = e.world.thangMap[@thang.id]
|
||||
@$el.toggle Boolean(currentThang.exists)
|
||||
@$el.toggleClass 'dead', currentThang.health <= 0 if currentThang.exists
|
||||
exists = Boolean currentThang.exists
|
||||
if @thangDidExist isnt exists
|
||||
@$el.toggle exists
|
||||
@thangDidExist = exists
|
||||
dead = exists and currentThang.health <= 0
|
||||
if @thangWasDead isnt dead
|
||||
@$el.toggleClass 'dead', dead
|
||||
@thangWasDead = dead
|
||||
|
||||
destroy: ->
|
||||
@avatar?.destroy()
|
||||
|
|
Loading…
Add table
Reference in a new issue