Added keyboard shortcuts to move, resize, minor-rotate, and toggle collision for Thangs in the level editor. Fixed some issues with stretchy Thangs and collision shapes not updating. Fixed #1699. Fixed #57. Colored collision overlays according to collision categories.

This commit is contained in:
Nick Winter 2014-12-20 13:39:40 -08:00
parent a52bf08792
commit e4c6d07a4a
5 changed files with 88 additions and 29 deletions

View file

@ -287,21 +287,21 @@ module.exports = Lank = class Lank extends CocoClass
# Let the pending flags know we're here (but not this call stack, they need to delete themselves, and we may be iterating sprites).
_.defer => Backbone.Mediator.publish 'surface:flag-appeared', sprite: @
updateScale: ->
updateScale: (force) ->
return unless @sprite
if @thangType.get('matchWorldDimensions') and @thang
if @thang.width isnt @lastThangWidth or @thang.height isnt @lastThangHeight
if @thangType.get('matchWorldDimensions') and @thang and @options.camera
if force or @thang.width isnt @lastThangWidth or @thang.height isnt @lastThangHeight or @thang.rotation isnt @lastThangRotation
bounds = @sprite.getBounds()
return unless bounds
@sprite.scaleX = @thang.width * Camera.PPM / bounds.width
@sprite.scaleY = @thang.height * Camera.PPM * @options.camera.y2x / bounds.height
@sprite.regX = bounds.width / 2
@sprite.regY = bounds.height / 2
@sprite.scaleX = @thang.width * Camera.PPM / bounds.width * (@options.camera.y2x + (1 - @options.camera.y2x) * Math.abs Math.cos @thang.rotation)
@sprite.scaleY = @thang.height * Camera.PPM / bounds.height * (@options.camera.y2x + (1 - @options.camera.y2x) * Math.abs Math.sin @thang.rotation)
@sprite.regX = bounds.width * 3 / 4 # Why not / 2? I don't know.
@sprite.regY = bounds.height * 3 / 4 # Why not / 2? I don't know.
unless @thang.spriteName is 'Beam'
@sprite.scaleX *= @thangType.get('scale') ? 1
@sprite.scaleY *= @thangType.get('scale') ? 1
[@lastThangWidth, @lastThangHeight] = [@thang.width, @thang.height]
[@lastThangWidth, @lastThangHeight, @lastThangRotation] = [@thang.width, @thang.height, @thang.rotation]
return
scaleX = scaleY = 1

View file

@ -500,6 +500,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
lank.setSprite(sprite)
lank.update(true)
@container.addChild(sprite)
lank.updateScale true if lank.thangType.get 'matchWorldDimensions' # Otherwise it's at the wrong scale for some reason.
renderGroupingKey: (thangType, grouping, colorConfig) ->
key = thangType.get('slug')

View file

@ -216,11 +216,19 @@ module.exports = class Mark extends CocoClass
buildDebug: ->
shapeName = if @lank.thang.shape in ['ellipsoid', 'disc'] then 'ellipse' else 'rect'
key = "#{shapeName}-debug"
key = "#{shapeName}-debug-#{@lank.thang.collisionCategory}"
DEBUG_SIZE = 10
unless key in @layer.spriteSheet.getAnimations()
shape = new createjs.Shape()
shape.graphics.beginFill 'rgba(171,205,239,0.5)'
debugColor = {
none: 'rgba(224,255,239,0.25)'
ground: 'rgba(239,171,205,0.5)'
air: 'rgba(131,205,255,0.5)'
ground_and_air: 'rgba(2391,140,239,0.5)'
obstacles: 'rgba(88,88,88,0.5)'
dead: 'rgba(89,171,100,0.25)'
}[@lank.thang.collisionCategory] or 'rgba(171,205,239,0.5)'
shape.graphics.beginFill debugColor
bounds = [-DEBUG_SIZE / 2, -DEBUG_SIZE / 2, DEBUG_SIZE, DEBUG_SIZE]
if shapeName is 'ellipse'
shape.graphics.drawEllipse bounds...
@ -232,9 +240,11 @@ module.exports = class Mark extends CocoClass
@sprite = new createjs.Sprite(@layer.spriteSheet)
@sprite.gotoAndStop(key)
PX = 3
[w, h] = [Math.max(PX, @lank.thang.width * Camera.PPM), Math.max(PX, @lank.thang.height * Camera.PPM) * @camera.y2x] # TODO: doesn't work with rotation
w = Math.max(PX, @lank.thang.width * Camera.PPM) * (@camera.y2x + (1 - @camera.y2x) * Math.abs Math.cos @lank.thang.rotation)
h = Math.max(PX, @lank.thang.height * Camera.PPM) * (@camera.y2x + (1 - @camera.y2x) * Math.abs Math.sin @lank.thang.rotation)
@sprite.scaleX = w / (@layer.resolutionFactor * DEBUG_SIZE)
@sprite.scaleY = h / (@layer.resolutionFactor * DEBUG_SIZE)
@sprite.rotation = -@lank.thang.rotation * 180 / Math.PI
buildSprite: ->
if _.isString @thangType

View file

@ -57,14 +57,23 @@ module.exports = class ThangsTabView extends CocoView
shortcuts:
'esc': 'selectAddThang'
'delete, del, backspace': 'deleteSelectedExtantThang'
'left': -> @moveAddThangSelection -1
'right': -> @moveAddThangSelection 1
'ctrl+z, ⌘+z': 'undo'
'ctrl+shift+z, ⌘+shift+z': 'redo'
'alt+left': -> @rotateSelectedThangBy(Math.PI)
'alt+right': -> @rotateSelectedThangBy(0)
'alt+up': -> @rotateSelectedThangBy(-Math.PI/2)
'alt+down': -> @rotateSelectedThangBy(Math.PI/2)
'alt+c': 'toggleSelectedThangCollision'
'left': -> @moveSelectedThangBy -1, 0
'right': -> @moveSelectedThangBy 1, 0
'up': -> @moveSelectedThangBy 0, 1
'down': -> @moveSelectedThangBy 0, -1
'alt+left': -> @rotateSelectedThangTo Math.PI unless key.shift
'alt+right': -> @rotateSelectedThangTo 0 unless key.shift
'alt+up': -> @rotateSelectedThangTo -Math.PI / 2
'alt+down': -> @rotateSelectedThangTo Math.PI / 2
'alt+shift+left': -> @rotateSelectedThangBy Math.PI / 16
'alt+shift+right': -> @rotateSelectedThangBy -Math.PI / 16
'shift+left': -> @resizeSelectedThangBy -1, 0
'shift+right': -> @resizeSelectedThangBy 1, 0
'shift+up': -> @resizeSelectedThangBy 0, 1
'shift+down': -> @resizeSelectedThangBy 0, -1
constructor: (options) ->
super options
@ -516,7 +525,7 @@ module.exports = class ThangsTabView extends CocoView
prefix += segment
if not @thangsTreema.get(prefix) then @thangsTreema.set(prefix, {})
onThangsChanged: =>
onThangsChanged: (skipSerialization) =>
return if @hush
# keep the thangs in the same order as before, roughly
@ -527,6 +536,7 @@ module.exports = class ThangsTabView extends CocoView
@level.set 'thangs', thangs
return if @editThangView
return if skipSerialization
serializedLevel = @level.serialize @supermodel, null, null, true
try
@world.loadFromLevel serializedLevel, false
@ -634,18 +644,56 @@ module.exports = class ThangsTabView extends CocoView
onClickRotationButton: (e) ->
$('#contextmenu').hide()
rotation = parseFloat($(e.target).closest('button').data('rotation'))
@rotateSelectedThangBy rotation * Math.PI
@rotateSelectedThangTo rotation * Math.PI
modifySelectedThangComponentConfig: (thang, componentOriginal, modificationFunction) ->
return unless thang
@hush = true
thangData = @getThangByID thang.id
thangData = $.extend true, {}, thangData
unless component = _.find thangData.components, {original: componentOriginal}
component = original: componentOriginal, config: {}
thangData.components.push component
modificationFunction component
@thangsTreema.set @pathForThang(thangData), thangData
@hush = false
@onThangsChanged true
thang.stateChanged = true
lank = @surface.lankBoss.lanks[thang.id]
lank.update true
lank.marks.debug?.destroy()
lank.marks.debug = null
lank.setDebug true
rotateSelectedThangTo: (radians) ->
@modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.PhysicalID, (component) =>
component.config.rotation = radians
@selectedExtantThang.rotation = component.config.rotation
rotateSelectedThangBy: (radians) ->
return unless @selectedExtantThang
@hush = true
thangData = @getThangByID(@selectedExtantThang.id)
thangData = $.extend(true, {}, thangData)
component = _.find thangData.components, {original: LevelComponent.PhysicalID}
component.config.rotation = radians
@thangsTreema.set(@pathForThang(thangData), thangData)
@hush = false
@onThangsChanged()
@modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.PhysicalID, (component) =>
component.config.rotation = ((component.config.rotation ? 0) + radians) % (2 * Math.PI)
@selectedExtantThang.rotation = component.config.rotation
moveSelectedThangBy: (xDir, yDir) ->
@modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.PhysicalID, (component) =>
component.config.pos.x += 0.5 * xDir
component.config.pos.y += 0.5 * yDir
@selectedExtantThang.pos.x = component.config.pos.x
@selectedExtantThang.pos.y = component.config.pos.y
resizeSelectedThangBy: (xDir, yDir) ->
@modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.PhysicalID, (component) =>
component.config.width = (component.config.width ? 4) + 0.5 * xDir
component.config.height = (component.config.height ? 4) + 0.5 * yDir
@selectedExtantThang.width = component.config.width
@selectedExtantThang.height = component.config.height
toggleSelectedThangCollision: ->
@modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.CollidesID, (component) =>
component.config ?= {}
component.config.collisionCategory = if component.config.collisionCategory is 'none' then 'ground' else 'none'
@selectedExtantThang.collisionCategory = component.config.collisionCategory
toggleThangsContainer: (e) ->
$('#all-thangs').toggleClass('hide')

View file

@ -63,7 +63,7 @@ setupExpressMiddleware = (app) ->
else
express.logger.format('dev', developmentLogging)
app.use(express.logger('dev'))
app.use(express.static(path.join(__dirname, 'public'), maxAge: 30 * 60 * 1000))
app.use(express.static(path.join(__dirname, 'public'), maxAge: 0)) # CloudFlare overrides maxAge, and we don't want local development caching.
app.use(useragent.express())
app.use(express.favicon())