diff --git a/.swift-version b/.swift-version index 8c50098d..a3ec5a4b 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3.1 +3.2 diff --git a/.travis.yml b/.travis.yml index ce3d49f7..d8955617 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: swift -osx_image: xcode8.0 +osx_image: xcode9.0 xcode_project: SKTiled.xcodeproj xcode_scheme: SKTiled-iOS branches: diff --git a/CHANGELOG.md b/CHANGELOG.md index 904bb560..a8b8fe7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ Change Log - add `SKTilesetData.frameAt(index:)` - add `SKTilesetData.setTexture(_:forFrame:)` - add `SKTilesetData.setDuration(interval:forFrame:)` +- add `SKTileObject.tileData` property +- add `SKTiledSceneCamera.clampZoomValue` +- add `SKTiledSceneCamera.zoomClamping` property - remove `SKTile.pauseAnimation` diff --git a/Demo/SKTiledDemoScene.swift b/Demo/SKTiledDemoScene.swift index ea6ea63d..51dc0248 100644 --- a/Demo/SKTiledDemoScene.swift +++ b/Demo/SKTiledDemoScene.swift @@ -33,10 +33,12 @@ public class SKTiledDemoScene: SKTiledScene { internal var selected: [SKTiledLayerObject] = [] internal var clickshapes: Set = [] - internal var pathshapes: Set = [] internal var cleanup: Set = [] - internal var graphCoordinates: [CGPoint] = [] + internal var plotPathfindingPath: Bool = true + internal var graphStartCoordinate: CGPoint? + internal var graphEndCoordinate: CGPoint? + internal var currentPath: [GKGridGraphNode] = [] /// Cleanup tile shapes queue @@ -49,7 +51,9 @@ public class SKTiledDemoScene: SKTiledScene { didSet { guard oldValue != liveMode else { return } self.cleanupTileShapes() - self.graphCoordinates = [] + self.cleanupPathfindingShapes() + self.graphStartCoordinate = nil + self.graphEndCoordinate = nil } } @@ -88,6 +92,12 @@ public class SKTiledDemoScene: SKTiledScene { updateHud(tilemap) } + /** + Return tile nodes at the given point. + + - parameter coord: `CGPoint` event point. + - returns: `[SKTile]` tile nodes. + */ func getTilesAt(coord: CGPoint) -> [SKTile] { var result: [SKTile] = [] guard let tilemap = tilemap else { return result } @@ -100,6 +110,12 @@ public class SKTiledDemoScene: SKTiledScene { return result } + /** + Return renderable nodes (tile & tile objects) at the given point. + + - parameter point: `CGPoint` event point. + - returns: `[SKNode]` renderable nodes. + */ func renderableNodesAt(point: CGPoint) -> [SKNode] { var result: [SKNode] = [] let nodes = self.nodes(at: point) @@ -191,13 +207,6 @@ public class SKTiledDemoScene: SKTiledScene { _ = lowerGraphLayer.initializeGraph(walkable: walkableTiles) } - - - case "graphtest-8x8.tmx": - if let graphLayer = tilemap.tileLayers(named: "Graph").first { - _ = graphLayer.initializeGraph(walkable: walkableTiles) - } - case "pacman.tmx": if let graphLayer = tilemap.tileLayers(named: "Graph").first { if let graph = graphLayer.initializeGraph(walkable: walkableTiles) { @@ -211,20 +220,11 @@ public class SKTiledDemoScene: SKTiledScene { } } - case "level-ramps.tmx", "level-rivets.tmx": - //log("setting up Donkey Kong level: \(baseFilename)", level: .info) - if let actorsGroup = tilemap.objectGroups(named: "Actors").first { - log("found actors group", level: .info) - let actorObjects = actorsGroup.getObjects() - for object in actorObjects { - print("object: \(object)") - } + case "roguelike-16x16.tmx": + if let graphLayer = tilemap.tileLayers(named: "Graph").first { + _ = graphLayer.initializeGraph(walkable: walkableTiles) } - case "line-widths.tmx": - - let objectsLayer = tilemap.objectGroups(named: "objects1").first! - objectsLayer.lineWidth = 4 default: return @@ -277,6 +277,10 @@ public class SKTiledDemoScene: SKTiledScene { NotificationCenter.default.post(name: Notification.Name(rawValue: "updateDebugLabels"), object: nil, userInfo: ["isolatedInfo": msg]) } + public func updateCoordinateInfo(msg: String) { + NotificationCenter.default.post(name: Notification.Name(rawValue: "updateDebugLabels"), object: nil, userInfo: ["coordinateInfo": msg]) + } + /** Send a command to the UI to update status. @@ -321,12 +325,13 @@ public class SKTiledDemoScene: SKTiledScene { */ func plotNavigationPath() { currentPath = [] - guard (graphCoordinates.count == 2) else { return } + //guard (graphCoordinates.count == 2) else { return } + guard let startCoord = graphStartCoordinate, + let endCoord = graphEndCoordinate else { return } - // cleanup - let startPoint = graphCoordinates.first!.toVec2 - let endPoint = graphCoordinates[1].toVec2 + let startPoint = startCoord.toVec2 + let endPoint = endCoord.toVec2 for (_, graph) in graphs { if let startNode = graph.node(atGridPosition: startPoint) { @@ -337,6 +342,49 @@ public class SKTiledDemoScene: SKTiledScene { } } + /** + Visualize the current grid graph path with a line. + */ + func drawCurrentPath(withColor: SKColor = TiledObjectColors.lime) { + guard let worldNode = worldNode, + let tilemap = tilemap else { return } + guard (currentPath.count > 2) else { return } + + worldNode.childNode(withName: "CURRENT_PATH")?.removeFromParent() + + // line dimensions + let headWidth: CGFloat = tilemap.tileSize.height + let lineWidth: CGFloat = tilemap.tileSize.halfWidth / 4 + + let lastZPosition = tilemap.lastZPosition + (tilemap.zDeltaForLayers * 4) + var points: [CGPoint] = [] + + for node in currentPath { + let nodePosition = worldNode.convert(tilemap.pointForCoordinate(vec2: node.gridPosition), from: tilemap.defaultLayer) + points.append(nodePosition) + } + + // path shape + let path = polygonPath(points, threshold: 16) + let shape = SKShapeNode(path: path) + shape.isAntialiased = false + shape.lineWidth = lineWidth * 2 + shape.strokeColor = withColor + shape.fillColor = .clear + + worldNode.addChild(shape) + shape.zPosition = lastZPosition + shape.name = "CURRENT_PATH" + + // arrowhead shape + let arrow = arrowFromPoints(startPoint: points[points.count - 2], endPoint: points.last!, tailWidth: lineWidth, headWidth: headWidth, headLength: headWidth) + let arrowShape = SKShapeNode(path: arrow) + arrowShape.strokeColor = .clear + arrowShape.fillColor = withColor + shape.addChild(arrowShape) + arrowShape.zPosition = lastZPosition + } + /** Cleanup all tile shapes outside of the given coordinate. @@ -384,14 +432,9 @@ public class SKTiledDemoScene: SKTiledScene { Cleanup all tile shapes representing the current path. */ open func cleanupPathfindingShapes() { - // clean the current path shapes... - for pathshape in self.pathshapes { - let fadeAction = SKAction.fadeOut(withDuration: 0.2) - pathshape.run(fadeAction, completion: { - self.pathshapes.remove(pathshape) - self.cleanup.insert(pathshape) - }) - } + // cleanup pathfinding shapes + guard let worldNode = worldNode else { return } + worldNode.childNode(withName: "CURRENT_PATH")?.removeFromParent() } /** @@ -421,6 +464,17 @@ public class SKTiledDemoScene: SKTiledScene { tile.removeFromParent() } } + + + var coordinateMessage = "" + if let graphStartCoordinate = graphStartCoordinate { + coordinateMessage += "Start: \(graphStartCoordinate.shortDescription)" + if (currentPath.isEmpty == false) { + coordinateMessage += ", \(currentPath.count) nodes" + } + } + + updateCoordinateInfo(msg: coordinateMessage) } // MARK: - Delegate Callbacks @@ -515,40 +569,20 @@ extension SKTiledDemoScene { // double click if (event.clickCount > 1) { + plotPathfindingPath = true + let hasStartCoordinate: Bool = (graphStartCoordinate != nil) - if graphCoordinates.contains(coord) { - cleanupPathfindingShapes() - return - } - - graphCoordinates.append(coord) - if graphCoordinates.count > 2 { + let eventMessage = (hasStartCoordinate == true) ? "clearing coordinate" : "setting coordinate: \(coord.shortDescription)" + graphStartCoordinate = (hasStartCoordinate == true) ? nil : coord + updateCommandString(eventMessage) + if hasStartCoordinate == true { cleanupPathfindingShapes() - graphCoordinates = graphCoordinates.reversed()[0...1].reversed() - } - - if (graphCoordinates.count == 2) { - plotNavigationPath() - if (currentPath.isEmpty == false) { - for node in currentPath { - let xcoord = Int(node.gridPosition.x) - let ycoord = Int(node.gridPosition.y) - - var nodeWeight: Float = 1 - if let weightedNode = node as? SKTiledGraphNode { - nodeWeight = weightedNode.weight - } - - if let tile = addTileToWorld(xcoord, ycoord, role: .pathfinding, weight: nodeWeight) { - self.pathshapes.insert(tile) - } - } - } } // single click } else { + plotPathfindingPath = false // highlight the current coordinate if let tile = addTileToWorld(Int(coord.x), Int(coord.y), role: .coordinate) { self.clickshapes.insert(tile) @@ -590,6 +624,15 @@ extension SKTiledDemoScene { let coord = defaultLayer.coordinateAtMouseEvent(event: event) let validCoord = defaultLayer.isValid(Int(coord.x), Int(coord.y)) + graphEndCoordinate = (validCoord == true) ? coord : nil + + if let endCoord = graphEndCoordinate { + if (plotPathfindingPath == true) { + plotNavigationPath() + drawCurrentPath(withColor: tilemap.navigationColor) + } + } + // query nodes under the cursor to update the properties label var propertiesInfoString = "--" @@ -639,7 +682,6 @@ extension SKTiledDemoScene { mouseTracker.coord = coord mouseTracker.isValid = validCoord - if (tileShapesUnderCursor.isEmpty) { if (liveMode == true) && (isPaused == false) { _ = self.addTileToWorld(Int(coord.x), Int(coord.y), role: .highlight) @@ -999,5 +1041,22 @@ extension SKTiledDemoScene { self.speed -= 0.5 updateCommandString("scene speed: \(speed.roundTo())", duration: 1.0) } + + // 'c' adjusts the camera zoom clamp value + if eventKey == 0x8 { + var newClampValue: CameraZoomClamping = .none + switch cameraNode.zoomClamping { + case .none: + newClampValue = .tenth + case .tenth: + newClampValue = .quarter + case .quarter: + newClampValue = .half + case .half: + newClampValue = .none + } + self.cameraNode.zoomClamping = newClampValue + updateCommandString("camera zoom clamping: \(newClampValue)", duration: 1.0) + } } } diff --git a/Resources/roguelike-16x16-anim.png b/Resources/roguelike-16x16-anim.png index 8a23b9db..76eedd95 100644 Binary files a/Resources/roguelike-16x16-anim.png and b/Resources/roguelike-16x16-anim.png differ diff --git a/Resources/roguelike-16x16.tmx b/Resources/roguelike-16x16.tmx index 96f6ca80..7f63ff06 100644 --- a/Resources/roguelike-16x16.tmx +++ b/Resources/roguelike-16x16.tmx @@ -1,9 +1,10 @@ - + + @@ -77,4 +78,9 @@ + + + eJztl1EKAjEMRP3y/sdcbyF+LIRhJplkW0XYQtDdtsnrJJH6ej4er9tu+yP7jF8zdDh38x6LGM9RaZ2d5wCLa6ec2ZhyIq/i7OTO4ax8nnMHcDBNd3HiJ9ur2JiG3bzH+NXo9IbiZO9Xcjq+kCPyIBc+OzGcfLvnVbyshpVvpU+X03lX5SbjvNJHqlYmOar6qIrtcMZn/O4M5FQxrnCyfVc5q5rp5mkHZ+bPiYH5cPxNOF3fDudKRsa5s993clZx45xay9a4rBhP9bvDqZ6rmF3G2EfZGrcOos/s/jDlzHTq1Oqk5h3tmZ6urnENrp/cwVhcdZ+qrNI9nr3LxvxVv+2T3DNONTINWV5wz1VO5t/lZLqyHH6bE/PKtO32Y3euw5n5wzsSy4fLyeYVJ/vP0znHRMtMg5WmarfLqbSc+HDvbmy+yzq5f50xqr2oq1ufinOSB+RkDOi3qofVerqcnTr4Fuduuzlvzl9xvgE4egCv + + diff --git a/Resources/roguelike-16x16.tsx b/Resources/roguelike-16x16.tsx index 4099ba1e..866b88a5 100644 --- a/Resources/roguelike-16x16.tsx +++ b/Resources/roguelike-16x16.tsx @@ -498,4 +498,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/SKTiled.podspec b/SKTiled.podspec index b522e90a..c31f11c1 100644 --- a/SKTiled.podspec +++ b/SKTiled.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SKTiled" - s.version = "1.15" + s.version = "1.16" s.summary = "SKTiled is a framework for using Tiled content with Apple's SpriteKit." s.description = <<-DESC SKTiled is a framework for using Tiled content with Apple's SpriteKit, allowing the creation of game assets from .tmx files. @@ -10,12 +10,10 @@ Pod::Spec.new do |s| s.license = { :type => 'MIT', :file => 'LICENSE.md' } s.ios.deployment_target = '9.0' - s.osx.deployment_target = '10.11' + s.osx.deployment_target = '10.12' s.source = { :git => "https://github.com/mfessenden/SKTiled.git", :tag => s.version } s.source_files = 'Sources/*.swift' s.requires_arc = true s.library = 'z' - s.preserve_path = 'zlib/*' - s.pod_target_xcconfig = { 'SWIFT_INCLUDE_PATHS' => '$(PODS_ROOT)/SKTiled/zlib' } end diff --git a/SKTiled.xcodeproj/project.pbxproj b/SKTiled.xcodeproj/project.pbxproj index 44fbfc32..af556ffc 100644 --- a/SKTiled.xcodeproj/project.pbxproj +++ b/SKTiled.xcodeproj/project.pbxproj @@ -91,8 +91,6 @@ 4C88E1E71D8A4EBE00FCCFA3 /* SKTileObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C88E1D91D8A4EBE00FCCFA3 /* SKTileObject.swift */; }; 4C88E1E81D8A4EBE00FCCFA3 /* SKTileset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C88E1DA1D8A4EBE00FCCFA3 /* SKTileset.swift */; }; 4C88E1EA1D8A4EBE00FCCFA3 /* SKTilesetData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C88E1DC1D8A4EBE00FCCFA3 /* SKTilesetData.swift */; }; - 4C8E42E01DC26FD1002C76DA /* zlib in Resources */ = {isa = PBXBuildFile; fileRef = 4C8E42DF1DC26FD1002C76DA /* zlib */; }; - 4C8E42E11DC26FD1002C76DA /* zlib in Resources */ = {isa = PBXBuildFile; fileRef = 4C8E42DF1DC26FD1002C76DA /* zlib */; }; 4C96C0541F53A09F00CE1DEB /* alter.png in Resources */ = {isa = PBXBuildFile; fileRef = 4C96C0141F53A09F00CE1DEB /* alter.png */; }; 4C96C0551F53A09F00CE1DEB /* alter.png in Resources */ = {isa = PBXBuildFile; fileRef = 4C96C0141F53A09F00CE1DEB /* alter.png */; }; 4C96C0561F53A09F00CE1DEB /* backgroundArch.png in Resources */ = {isa = PBXBuildFile; fileRef = 4C96C0151F53A09F00CE1DEB /* backgroundArch.png */; }; @@ -259,8 +257,6 @@ /* Begin PBXFileReference section */ 4C01D3A61F1E07AB00FFAD28 /* SKTiled+GameplayKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SKTiled+GameplayKit.swift"; sourceTree = ""; }; 4C0E59501DC046E700921F75 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; - 4C0EE1E51DC116DB00A052E4 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; - 4C0EE1E71DC116E300A052E4 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; }; 4C17747F1D909740000C0AFD /* SKTiledDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SKTiledDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4C3117D81EF84FCF00892D9E /* SKTiled+Debug.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SKTiled+Debug.swift"; sourceTree = ""; }; 4C3184E71F4DCB4200950BBF /* staggered-64x33.tmx */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = text.xml; path = "staggered-64x33.tmx"; sourceTree = ""; }; @@ -299,7 +295,6 @@ 4C88E1D91D8A4EBE00FCCFA3 /* SKTileObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKTileObject.swift; sourceTree = ""; }; 4C88E1DA1D8A4EBE00FCCFA3 /* SKTileset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKTileset.swift; sourceTree = ""; }; 4C88E1DC1D8A4EBE00FCCFA3 /* SKTilesetData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKTilesetData.swift; sourceTree = ""; }; - 4C8E42DF1DC26FD1002C76DA /* zlib */ = {isa = PBXFileReference; lastKnownFileType = folder; path = zlib; sourceTree = SOURCE_ROOT; }; 4C96C0141F53A09F00CE1DEB /* alter.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = alter.png; sourceTree = ""; }; 4C96C0151F53A09F00CE1DEB /* backgroundArch.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = backgroundArch.png; sourceTree = ""; }; 4C96C0161F53A09F00CE1DEB /* backgroundMountain.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = backgroundMountain.png; sourceTree = ""; }; @@ -419,32 +414,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 4C0A864B1DECC1BA008EF817 /* iOS */ = { - isa = PBXGroup; - children = ( - 4C0EE1E51DC116DB00A052E4 /* libz.tbd */, - ); - name = iOS; - sourceTree = ""; - }; - 4C0A864C1DECC1C0008EF817 /* macOS */ = { - isa = PBXGroup; - children = ( - 4C0EE1E71DC116E300A052E4 /* libz.tbd */, - ); - name = macOS; - sourceTree = ""; - }; - 4C0E59511DC053CA00921F75 /* Libraries */ = { - isa = PBXGroup; - children = ( - 4C0A864C1DECC1C0008EF817 /* macOS */, - 4C0A864B1DECC1BA008EF817 /* iOS */, - ); - name = Libraries; - path = ..; - sourceTree = ""; - }; 4C440B971DAE911000DEC9A4 /* Demo */ = { isa = PBXGroup; children = ( @@ -499,16 +468,6 @@ path = framework; sourceTree = ""; }; - 4C6480BE1DC1231C006F4CB3 /* Modules */ = { - isa = PBXGroup; - children = ( - 4C0E59511DC053CA00921F75 /* Libraries */, - 4C8E42DF1DC26FD1002C76DA /* zlib */, - ); - name = Modules; - path = Sources; - sourceTree = ""; - }; 4C7DD6241F50E16600C3FE2D /* tsx */ = { isa = PBXGroup; children = ( @@ -619,7 +578,6 @@ 4C440B971DAE911000DEC9A4 /* Demo */, 4C440BAB1DAE913A00DEC9A4 /* iOS */, 4C440BA11DAE913000DEC9A4 /* macOS */, - 4C6480BE1DC1231C006F4CB3 /* Modules */, 4CBA61661D8A048200B31FC1 /* Products */, ); sourceTree = ""; @@ -792,7 +750,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0800; - LastUpgradeCheck = 0830; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Michael Fessenden"; TargetAttributes = { 4C17747E1D909740000C0AFD = { @@ -846,7 +804,6 @@ 4C96C0981F53A09F00CE1DEB /* platformBase1.png in Resources */, 4C96C07C1F53A09F00CE1DEB /* flare.png in Resources */, 4C96C08C1F53A09F00CE1DEB /* keyRedStroked.png in Resources */, - 4C8E42E11DC26FD1002C76DA /* zlib in Resources */, 4C96C0A01F53A09F00CE1DEB /* platformBlock1.png in Resources */, 4C96C0D01F53A09F00CE1DEB /* window2.png in Resources */, 4C96C0701F53A09F00CE1DEB /* doorGreenStroke.png in Resources */, @@ -979,7 +936,6 @@ 4C96C0E31F53BD8000CE1DEB /* pacman.tmx in Resources */, 4C96C0C71F53A09F00CE1DEB /* trap.png in Resources */, 4C96C0771F53A09F00CE1DEB /* earthWall.png in Resources */, - 4C8E42E01DC26FD1002C76DA /* zlib in Resources */, 4C96C09F1F53A09F00CE1DEB /* platformBase4.png in Resources */, 4C440B9B1DAE911000DEC9A4 /* Assets.xcassets in Resources */, 4C96C0811F53A09F00CE1DEB /* gemRedStroked.png in Resources */, @@ -1372,14 +1328,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -1401,7 +1363,8 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MTL_ENABLE_DEBUG_INFO = YES; - OTHER_LDFLAGS = ""; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-lz"; SDKROOT = iphoneos; SWIFT_INCLUDE_PATHS = zlib; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -1417,14 +1380,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -1440,7 +1409,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = ""; + OTHER_LDFLAGS = "-lz"; SDKROOT = iphoneos; SWIFT_INCLUDE_PATHS = zlib; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/SKTiled.xcodeproj/xcshareddata/xcschemes/SKTiled-iOS.xcscheme b/SKTiled.xcodeproj/xcshareddata/xcschemes/SKTiled-iOS.xcscheme index 55bd5e3d..2a0c1655 100644 --- a/SKTiled.xcodeproj/xcshareddata/xcschemes/SKTiled-iOS.xcscheme +++ b/SKTiled.xcodeproj/xcshareddata/xcschemes/SKTiled-iOS.xcscheme @@ -1,6 +1,6 @@ @@ -36,6 +37,7 @@ buildConfiguration = "Release" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/SKTiled.xcodeproj/xcshareddata/xcschemes/SKTiled-macOS.xcscheme b/SKTiled.xcodeproj/xcshareddata/xcschemes/SKTiled-macOS.xcscheme index 027f067a..43c2c25a 100644 --- a/SKTiled.xcodeproj/xcshareddata/xcschemes/SKTiled-macOS.xcscheme +++ b/SKTiled.xcodeproj/xcshareddata/xcschemes/SKTiled-macOS.xcscheme @@ -1,6 +1,6 @@ @@ -36,6 +37,7 @@ buildConfiguration = "Release" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/Sources/SKTileLayer.swift b/Sources/SKTileLayer.swift index 86394544..bff70c2e 100644 --- a/Sources/SKTileLayer.swift +++ b/Sources/SKTileLayer.swift @@ -2348,6 +2348,16 @@ extension SKTiledLayerObject { return self.pointForCoordinate(coord: coord, offsetX: offset.x, offsetY: offset.y) } + /** + Returns a tile coordinate for a given vector_int2 coordinate. + + - parameter vec2: `int2` vector int2. + - returns: `CGPoint` position in layer. + */ + public func pointForCoordinate(vec2: int2) -> CGPoint { + return self.pointForCoordinate(coord: vec2.cgPoint) + } + /** Returns a tile coordinate for a given point in the layer. diff --git a/Sources/SKTileObject.swift b/Sources/SKTileObject.swift index c9e7d770..0f1e2712 100644 --- a/Sources/SKTileObject.swift +++ b/Sources/SKTileObject.swift @@ -123,6 +123,12 @@ open class SKTileObject: SKShapeNode, SKTiledObject { internal var anchorKey: String = "ANCHOR" internal var tile: SKTile? // optional tile + + /// Tile data (for tile objects). + open var tileData: SKTilesetData? { + return tile?.tileData + } + /// Object bounds color. open var frameColor: SKColor = TiledObjectColors.magenta diff --git a/Sources/SKTiled+Debug.swift b/Sources/SKTiled+Debug.swift index 94b648e3..9332191b 100644 --- a/Sources/SKTiled+Debug.swift +++ b/Sources/SKTiled+Debug.swift @@ -667,7 +667,6 @@ public enum LoggingLevel: Int { case success case status case info - case gcd case debug case custom } diff --git a/Sources/SKTiled+Extensions.swift b/Sources/SKTiled+Extensions.swift index 928cf917..2f275688 100644 --- a/Sources/SKTiled+Extensions.swift +++ b/Sources/SKTiled+Extensions.swift @@ -1190,15 +1190,50 @@ public func / (lhs: int2, rhs: int2) -> int2 { return int2(lhs.x / rhs.x, lhs.y / rhs.y) } - +/* public func /= (lhs: inout int2, rhs: int2) { lhs /= rhs } +*/ public func == (lhs: int2, rhs: int2) -> Bool { return (lhs.x == rhs.x) && (lhs.y == rhs.y) } + +extension vector_int2 { + + /** + Returns the difference vector to another in2. + + - parameter v: `vector_int2` coordinate. + - returns: `CGVector` difference between. + */ + public func delta(to v: int2) -> CGVector { + let dx = Float(x - v.x) + let dy = Float(y - v.y) + return CGVector(dx: Int(dx), dy: Int(dy)) + } + + /** + Returns true if the coordinate vector is contiguous to another vector. + + - parameter v: `vector_int2` coordinate. + - returns: `Bool` coordinates are contiguous. + */ + public func isContiguousTo(v: int2) -> Bool { + let dx = Float(x - v.x) + let dy = Float(y - v.y) + return sqrt((dx * dx) + (dy * dy)) == 1 + } + + /// Convert the int2 to CGPoint. + public var cgPoint: CGPoint { + return CGPoint(x: CGFloat(x), y: CGFloat(y)) + } +} + + // MARK: - Helper Functions public func floor(point: CGPoint) -> CGPoint { @@ -1654,7 +1689,6 @@ public func polygonPointArray(_ sides: Int, radius: CGSize, offset: CGFloat=0, o - parameter points: `[CGPoint]` polygon points. - parameter closed: `Bool` path should be closed. - - parameter origin: `CGPoint` origin point. - returns: `CGPath` path from the given points. */ public func polygonPath(_ points: [CGPoint], closed: Bool=true) -> CGPath { @@ -1698,7 +1732,7 @@ public func polygonPath(_ sides: Int, radius: CGSize, offset: CGFloat=0, origin: - parameter points: `[CGPoint]` polygon points. - parameter closed: `Bool` path should be closed. - parameter alpha: `CGFloat` curvature. - - returns: `CGPath` path from the given points. + - returns: `(CGPath, [CGPoint])` bezier path and control points. */ public func bezierPath(_ points: [CGPoint], closed: Bool = true, alpha: CGFloat = 1) -> (path: CGPath, points: [CGPoint]) { guard points.count > 1 else { return (CGMutablePath(), [CGPoint]()) } @@ -1762,6 +1796,84 @@ public func bezierPath(_ points: [CGPoint], closed: Bool = true, alpha: CGFloat } +/** + Takes an array of grid graph points and returns a path. Set threshold value to allow for gaps in the path. + + - parameter points: `[CGPoint]` path points. + - parameter threshold: `CGFloat` gap threshold size. + - returns: `CGPath` path from the given points. + */ +public func polygonPath(_ points: [CGPoint], threshold: CGFloat) -> CGPath { + let path = CGMutablePath() + var mpoints = points + let first = mpoints.remove(at: 0) + path.move(to: first) + var current = first + for p in mpoints { + let distToLast = CGFloat(p.distance(current)) + if (distToLast > threshold) { + path.move(to: p) + } else { + path.addLine(to: p) + } + current = p + } + return path +} + +/** + Given two points, create an arrowhead. + + - parameter startPoint: `CGPoint` first point. + - parameter endPoint: `CGPoint` last point. + - parameter tailWidth: `CGFloat` arrow tail width. + - parameter headWidth: `CGFloat` arrow head width. + - parameter headLength: `CGFloat` arrow head length. + - returns: `CGPath` path from the given points. + */ +public func arrowFromPoints(startPoint: CGPoint, + endPoint: CGPoint, + tailWidth: CGFloat, + headWidth: CGFloat, + headLength: CGFloat) -> CGPath { + + let length = CGFloat(hypotf(Float(endPoint.x) - Float(startPoint.x), Float(endPoint.y) - Float(startPoint.y))) + + // offset from start to end + let offsetX = startPoint.x - endPoint.x + let offsetY = startPoint.y - endPoint.y + let offset = CGPoint(x: offsetX, y: offsetY) + var points: [CGPoint] = [] + + let tailLength = length - headLength + + points.append(CGPoint(x: 0, y: tailWidth/2)) + points.append(CGPoint(x: tailLength, y: tailWidth)) + points.append(CGPoint(x: tailLength, y: headLength/2)) + points.append(CGPoint(x: length, y: 0)) + points.append(CGPoint(x: tailLength, y: -headWidth/2)) + points.append(CGPoint(x: tailLength, y: -tailWidth/2 )) + points.append(CGPoint(x: 0, y: -tailWidth/2)) + + let cosine = (endPoint.x - startPoint.x)/length + let sine = (endPoint.y - startPoint.y)/length + + var transform = CGAffineTransform() + transform.a = cosine + transform.b = sine + transform.c = -sine + transform.d = cosine + transform.tx = startPoint.x - offset.x + transform.ty = startPoint.y - offset.y + + + let path = CGMutablePath() + path.addLines(between: points, transform: transform) + path.closeSubpath() + return path +} + + /** Returns the device scale factor. diff --git a/Sources/SKTiledScene.swift b/Sources/SKTiledScene.swift index 75289756..a7b29f01 100644 --- a/Sources/SKTiledScene.swift +++ b/Sources/SKTiledScene.swift @@ -67,7 +67,7 @@ open class SKTiledScene: SKScene, SKPhysicsContactDelegate, SKTiledSceneDelegate private var lastUpdateTime: TimeInterval = 0 private let maximumUpdateDelta: TimeInterval = 1.0 / 60.0 - + /// Set the tilemap speed override open var speed: CGFloat { didSet { diff --git a/Sources/SKTiledSceneCamera.swift b/Sources/SKTiledSceneCamera.swift index cf25e581..f4bb97dc 100644 --- a/Sources/SKTiledSceneCamera.swift +++ b/Sources/SKTiledSceneCamera.swift @@ -21,7 +21,7 @@ import Cocoa protocol are notified of camera position & zoom changes. */ public protocol SKTiledSceneCameraDelegate: class { - + /** Called when the camera positon changes. @@ -70,6 +70,17 @@ public protocol SKTiledSceneCameraDelegate: class { #endif } +/** + Camera zoom rounding factor. Clamps the zoom value to nearest quarter or half percentage. + */ +public enum CameraZoomClamping: CGFloat { + case none = 0 + case half = 2 + case quarter = 4 + case tenth = 10 +} + + /** ## Overview ## @@ -100,6 +111,12 @@ public class SKTiledSceneCamera: SKCameraNode, Loggable { public var minZoom: CGFloat = 0.2 public var maxZoom: CGFloat = 5.0 public var isAtMaxZoom: Bool { return zoom == maxZoom } + /// Clamp factor to alleviate cracks in tilemap. + public var zoomClamping: CameraZoomClamping = .none { + didSet { + setCameraZoom(self.zoom) + } + } // logger public var loggingLevel: LoggingLevel = .info @@ -207,8 +224,11 @@ public class SKTiledSceneCamera: SKCameraNode, Loggable { - parameter scale: `CGFloat` zoom amount. */ public func setCameraZoom(_ scale: CGFloat, interval: TimeInterval=0) { - // clamp scaling - let zoomClamped = scale.clamped(minZoom, maxZoom) + // clamp scaling between min/max zoom + var zoomClamped = scale.clamped(minZoom, maxZoom) + + // round zoom value to alleviate cracking + zoomClamped = clampZoomValue(zoomClamped, factor: zoomClamping.rawValue) self.zoom = zoomClamped let zoomAction = SKAction.scale(to: zoomClamped, duration: interval) @@ -218,6 +238,7 @@ public class SKTiledSceneCamera: SKCameraNode, Loggable { } else { world.run(zoomAction) } + if let tilemap = (scene as? SKTiledScene)?.tilemap { tilemap.autoResize = false } @@ -251,6 +272,19 @@ public class SKTiledSceneCamera: SKCameraNode, Loggable { maxZoom = maximum } + /** + Clamp the camera scale to alleviate cracking. Default clamps float value to the nearest 0.25. + + - parameter value: `CGFloat` zoom value. + - parameter factor: `CGFloat` scale factor. + - returns: `CGFloat` clamped zoom value. + */ + internal func clampZoomValue(_ value: CGFloat, factor: CGFloat = 0) -> CGFloat { + guard factor != 0 else { return value } + let result = round(value * factor) / factor + return (result > 0) ? result : value + } + // MARK: - Bounds /** Update the camera bounds. @@ -416,15 +450,35 @@ public class SKTiledSceneCamera: SKCameraNode, Loggable { } + +extension CameraZoomClamping { + /// Minimum possible value. + public var minimum: CGFloat { + switch self { + case .half: + return 0.50 + case .quarter: + return 0.25 + case .tenth: + return 0.10 + default: + return 0 + } + } +} + + extension SKTiledSceneCamera { override public var description: String { guard let scene = scene else { return "Camera: "} let rect = CGRect(origin: scene.convert(position, from: self), size: bounds.size) - return "Camera: \(rect.roundTo()), zoom: \(zoom.roundTo())" + let clampString = (zoomClamping != .none) ? ", clamp: \(zoomClamping.minimum)" : "" + return "Camera: \(rect.roundTo()), zoom: \(zoom.roundTo())\(clampString)" } override public var debugDescription: String { - return "Camera: \(bounds.roundTo()), zoom: \(zoom.roundTo())" + let clampString = (zoomClamping != .none) ? ", clamp: \(zoomClamping.minimum)" : "" + return "Camera: \(bounds.roundTo()), zoom: \(zoom.roundTo())\(clampString)" } } diff --git a/Sources/SKTilemap+Properties.swift b/Sources/SKTilemap+Properties.swift index 8e3695d0..3676e5be 100644 --- a/Sources/SKTilemap+Properties.swift +++ b/Sources/SKTilemap+Properties.swift @@ -145,6 +145,10 @@ public extension SKTilemap { if ["nicename", "displayname"].contains(lattr) { displayName = value } + + if (lattr == "navigationcolor") { + navigationColor = SKColor(hexString: value) + } } if completion != nil { completion!() } diff --git a/Sources/SKTilemap.swift b/Sources/SKTilemap.swift index 9904b523..096e7225 100644 --- a/Sources/SKTilemap.swift +++ b/Sources/SKTilemap.swift @@ -328,7 +328,7 @@ public class SKTilemap: SKNode, SKTiledObject { public var gridColor: SKColor = TiledObjectColors.obsidian // color used to visualize the tile grid public var frameColor: SKColor = TiledObjectColors.obsidian // bounding box color public var highlightColor: SKColor = TiledObjectColors.lime // color used to highlight tiles - + public var navigationColor: SKColor = TiledObjectColors.lime // navigation graph color. internal var autoResize: Bool = false // indicates map should auto-resize when view changes /// dynamics @@ -716,6 +716,16 @@ public class SKTilemap: SKNode, SKTiledObject { return defaultLayer.pointForCoordinate(coord: coord, offsetX: offsetX, offsetY: offsetY) } + /** + Returns a tile coordinate for a given vector_int2 coordinate. + + - parameter vec2: `int2` vector int2. + - returns: `CGPoint` position in layer. + */ + public func pointForCoordinate(vec2: int2) -> CGPoint { + return defaultLayer.pointForCoordinate(vec2: vec2) + } + /** Returns a tile coordinate for a given point in the layer. diff --git a/macOS/Base.lproj/Main.storyboard b/macOS/Base.lproj/Main.storyboard index 50b8b852..c9642eb9 100644 --- a/macOS/Base.lproj/Main.storyboard +++ b/macOS/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -218,6 +218,9 @@ + + + @@ -236,7 +239,7 @@ - +