在Xcode 8中为SKTileMapNode的tile添加物理

时间:2016-10-04 02:25:40

标签: swift xcode sprite-kit skphysicsbody sktilemapnode

我正在学习Swift,作为一个项目,我正在开发一个类似于超级马里奥的基于平铺的2D游戏,我的角色会在那里走路并跳上瓷砖。

最新版本的Xcode和Sprite Kit可以直接在Xcode中创建Tile Map。

在新的Xcode和Sprite工具包的演示中,这个人演示了一个类似于我正在做的游戏。

https://developer.apple.com/videos/play/wwdc2016/610/(大约20分钟)。

他提到我给Tiles用户数据属性,并且在代码中我们搜索具有该用户数据的所有tile并给它们一些物理属性,以便角色可以碰撞或与它们交互(在我的情况下,我的字符不会掉落或穿过瓷砖)。

所以基本上,这个想法是给那些瓦片一个物理体,但这不能用SKphysicsBody来完成。所以必须有另一种方式,因为我是Swift的新手,我很想念它。

如果有人知道这一点,我会非常感谢你的帮助。

如果问题不清楚,请告诉我,因为我也是堆叠溢出的新手。

4 个答案:

答案 0 :(得分:1)

我不确定是否有一种确定的方法可以做到这一点......但是,有两种方法可以考虑如何尝试将物理应用于tilemap。

选项1:将SKNodes应用于地图上每个图块的每个位置,并根据该图块的内容和状态将适当的物理体应用于该SKNode。

选项2:使用每个图块的位置信息将一个物理实体数组添加到SKTileMapNode,并相应地定位每个图块。

我想象一个重力下降,马里奥风格的平台游戏,这种地形需要物理机构的地面:

enter image description here

取消了Apple的WWDC关于瓷砖和物理的成绩单:

  

而且你会注意到我在这里与瓷砖碰撞。

     

我通过利用我们可以提供的自定义用户数据来实现这一目标   我们的每一块瓷砖。

     

在这里,我将在我们的图块集中显示你。

     

在此处选择其中一个变体。

     

你可以看到我们这里有一些用户数据。

     

我只有一个名为edgeTile的值,它是一个布尔值,我设置了   到1。

     

所以,在代码中,我在这里的平台演示中浏览了瓷砖地图,   我正在寻找所有这些边缘瓷砖。

     

每当我找到一个时,我会创建一些物理数据来允许   玩家与它发生碰撞。

     

因为它只是在我们的瓷砖地图中,所以说我想要克服   这里有这么大的墙。

     

当我运行游戏时,你会发现我的家伙实际上并不能跳得很高   足以克服它。

     

他真的很想,因为那边的红色按钮看起来真的很棒   诱人的。

     

我真的想推动那件事。

     

所以,因为我们只是从我们的瓷砖生成物理数据   我们的用户数据,我们所能做的只是去这里擦除这些瓷砖   并再次构建和运行我们的游戏。

     

瓷砖现在已经消失了,我们现在可以在那里移动了。

     

我们没有必要改变代码或任何东西。

     

我们刚刚使用了从平铺图中提取的数据来设置我们的数据   瓦。

     

就这么简单。

听起来很简单,但需要比我更大的思维来弄清楚正在做什么,以及它是如何完成的。

答案 1 :(得分:1)

Apple员工Bobjt说here"正确的方法"是将用户数据添加到SKTileDefinition个对象,并使用它来识别和添加物理主体。

因此,您可以通过编程方式或在编辑器中将用户数据值添加到切片定义中,如下所示:

enter image description here

然后在代码中,您将检查每个切片定义以查看它是否具有用户数据值。如果是这样,那么您需要计算图块的位置并在新节点上添加物理主体,将其作为平铺图谱的父级。以下是Bobjt称之为"正确方法":

的代码
self.tileMap = self.childNode(withName: "Tile Map") as? SKTileMapNode  
guard let tileMap = self.tileMap else { fatalError("Missing tile map for the level") }  

let tileSize = tileMap.tileSize  
let halfWidth = CGFloat(tileMap.numberOfColumns) / 2.0 * tileSize.width  
let halfHeight = CGFloat(tileMap.numberOfRows) / 2.0 * tileSize.height  

for col in 0..<tileMap.numberOfColumns {  
    for row in 0..<tileMap.numberOfRows {  
        let tileDefinition = tileMap.tileDefinition(atColumn: col, row: row)  
        let isEdgeTile = tileDefinition?.userData?["edgeTile"] as? Bool  
        if (isEdgeTile ?? false) {  
            let x = CGFloat(col) * tileSize.width - halfWidth  
            let y = CGFloat(row) * tileSize.height - halfHeight  
            let rect = CGRect(x: 0, y: 0, width: tileSize.width, height: tileSize.height)  
            let tileNode = SKShapeNode(rect: rect)  
            tileNode.position = CGPoint(x: x, y: y)  
            tileNode.physicsBody = SKPhysicsBody.init(rectangleOf: tileSize, center: CGPoint(x: tileSize.width / 2.0, y: tileSize.height / 2.0))  
            tileNode.physicsBody?.isDynamic = false  
            tileNode.physicsBody?.collisionBitMask = playerCollisionMask | wallCollisionMask  
            tileNode.physicsBody?.categoryBitMask = wallCollisionMask  
            tileMap.addChild(tileNode)  
        }  
    }  
}  

就个人而言,我认为这种做法太挑剔了。我将在游戏中尝试不同的方法,如果有效,我会在这里发布。我真正喜欢的是Apple增强了tile map API,以便我们可以直接将物理主体添加到各个tile中。也许在这个过程中,他们可以优化引擎,以便各个图块上的物理主体自动合并,形成更大,更优的形状,以提高系统性能。

更新:我向Apple提交了request有关此问题的问题for whatever good might come of it

答案 2 :(得分:0)

我花了两天时间尝试不同的想法,但没有比我在my other answer发布的内容更好的结果。正如那里提到的那样,这是Apple工作人员推荐的方式,据我所知,让SpriteKit自动为你的所有瓷砖添加物理实体是最有效的方法。我测试了它,它的工作原理。 (虽然我仍然屏住呼吸,让苹果公司添加一种将物理身体放在瓷砖上的直接方式)。

但还有其他一些考虑因素。如果您遇到性能问题,因为场景中有太多物理实体,您可能想尝试其中一种其他方法。但是,它们比上述方法更耗时。可能有理由使用其中一种劳动密集型方法的唯一原因是,由于性能问题,您需要减少场景中物理实体的数量。否则,我认为&#34;自动&#34;上面提到的方法是我们最好的选择。

所以我在这里不详细说明因为我认为自动选项是最好的。如果您的游戏需要对系统资源更加吝啬,那么这些只是替代方法的想法。

替代方法#1 - 使用更少的物理机构

在编辑器中创建切片贴图。然后继续在编辑器中工作,将Color Sprites(SKSpriteNodes)拖动到需要物理实体的地图部分上。对节点进行整形,以便为需要物理实体的区域创建最大的矩形。这对于大型平坦表面(如墙壁,地板,天花板,平台,板条箱等)最有效。但是如果使用上面提到的自动方法,模拟中的物理实体的数量要少得多。

替代方法#2 - 不使用物理实体

这个想法可能需要更多的工作,但你可能完全避免使用物理机构。首先,在编辑器中创建切片贴图。分析您的地图以确定哪些标记标记了一个障碍,玩家不应该越过该障碍。将用户数据标识符分配给该类型的磁贴。对于不同类型的障碍,您需要不同类别的标识符,您可能还需要设计您的作品以适应此方法。

一旦你的障碍图块得到充分识别,就编写代码来检查播放器精灵当前占用的图块的用户数据值,并相应地限制精灵的移动。例如,如果玩家输入标记上边界的标题,则您的移动代码将不允许玩家精灵向上移动。同样,如果玩家输入标记最左边界的图块,则您的移动代码不会让玩家向左移动。

答案 3 :(得分:0)

或者,您可以将线条扫描算法应用于要提供SKPhysicsBody的图块。

您可以通过四个步骤进行操作;

  1. 遍历图块在SKTileMap中的位置。

  2. 找到彼此相邻的图块。

  3. 对于每组相邻的图块,收集:

    • 左下角坐标
    • 右上角坐标
  4. 绘制一个正方形,然后移至下一组图块,直到用完图块坐标为止。

Screenshot without visuals for comparison

Screenshot without visuals showing the physicsbodies

See my answer in a similar post on how to implement this.