我正在学习Swift,作为一个项目,我正在开发一个类似于超级马里奥的基于平铺的2D游戏,我的角色会在那里走路并跳上瓷砖。
最新版本的Xcode和Sprite Kit可以直接在Xcode中创建Tile Map。
在新的Xcode和Sprite工具包的演示中,这个人演示了一个类似于我正在做的游戏。
https://developer.apple.com/videos/play/wwdc2016/610/(大约20分钟)。
他提到我给Tiles用户数据属性,并且在代码中我们搜索具有该用户数据的所有tile并给它们一些物理属性,以便角色可以碰撞或与它们交互(在我的情况下,我的字符不会掉落或穿过瓷砖)。
所以基本上,这个想法是给那些瓦片一个物理体,但这不能用SKphysicsBody来完成。所以必须有另一种方式,因为我是Swift的新手,我很想念它。
如果有人知道这一点,我会非常感谢你的帮助。
如果问题不清楚,请告诉我,因为我也是堆叠溢出的新手。
答案 0 :(得分:1)
我不确定是否有一种确定的方法可以做到这一点......但是,有两种方法可以考虑如何尝试将物理应用于tilemap。
选项1:将SKNodes应用于地图上每个图块的每个位置,并根据该图块的内容和状态将适当的物理体应用于该SKNode。
选项2:使用每个图块的位置信息将一个物理实体数组添加到SKTileMapNode,并相应地定位每个图块。
我想象一个重力下降,马里奥风格的平台游戏,这种地形需要物理机构的地面:
而且你会注意到我在这里与瓷砖碰撞。
我通过利用我们可以提供的自定义用户数据来实现这一目标 我们的每一块瓷砖。
在这里,我将在我们的图块集中显示你。
在此处选择其中一个变体。
你可以看到我们这里有一些用户数据。
我只有一个名为edgeTile的值,它是一个布尔值,我设置了 到1。
所以,在代码中,我在这里的平台演示中浏览了瓷砖地图, 我正在寻找所有这些边缘瓷砖。
每当我找到一个时,我会创建一些物理数据来允许 玩家与它发生碰撞。
因为它只是在我们的瓷砖地图中,所以说我想要克服 这里有这么大的墙。
当我运行游戏时,你会发现我的家伙实际上并不能跳得很高 足以克服它。
他真的很想,因为那边的红色按钮看起来真的很棒 诱人的。
我真的想推动那件事。
所以,因为我们只是从我们的瓷砖生成物理数据 我们的用户数据,我们所能做的只是去这里擦除这些瓷砖 并再次构建和运行我们的游戏。
瓷砖现在已经消失了,我们现在可以在那里移动了。
我们没有必要改变代码或任何东西。
我们刚刚使用了从平铺图中提取的数据来设置我们的数据 瓦。
就这么简单。
听起来很简单,但需要比我更大的思维来弄清楚正在做什么,以及它是如何完成的。
答案 1 :(得分:1)
Apple员工Bobjt说here"正确的方法"是将用户数据添加到SKTileDefinition
个对象,并使用它来识别和添加物理主体。
因此,您可以通过编程方式或在编辑器中将用户数据值添加到切片定义中,如下所示:
然后在代码中,您将检查每个切片定义以查看它是否具有用户数据值。如果是这样,那么您需要计算图块的位置并在新节点上添加物理主体,将其作为平铺图谱的父级。以下是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;上面提到的方法是我们最好的选择。
所以我在这里不详细说明因为我认为自动选项是最好的。如果您的游戏需要对系统资源更加吝啬,那么这些只是替代方法的想法。
在编辑器中创建切片贴图。然后继续在编辑器中工作,将Color Sprites(SKSpriteNodes
)拖动到需要物理实体的地图部分上。对节点进行整形,以便为需要物理实体的区域创建最大的矩形。这对于大型平坦表面(如墙壁,地板,天花板,平台,板条箱等)最有效。但是如果使用上面提到的自动方法,模拟中的物理实体的数量要少得多。
这个想法可能需要更多的工作,但你可能完全避免使用物理机构。首先,在编辑器中创建切片贴图。分析您的地图以确定哪些标记标记了一个障碍,玩家不应该越过该障碍。将用户数据标识符分配给该类型的磁贴。对于不同类型的障碍,您需要不同类别的标识符,您可能还需要设计您的作品以适应此方法。
一旦你的障碍图块得到充分识别,就编写代码来检查播放器精灵当前占用的图块的用户数据值,并相应地限制精灵的移动。例如,如果玩家输入标记上边界的标题,则您的移动代码将不允许玩家精灵向上移动。同样,如果玩家输入标记最左边界的图块,则您的移动代码不会让玩家向左移动。
答案 3 :(得分:0)
或者,您可以将线条扫描算法应用于要提供SKPhysicsBody的图块。
您可以通过四个步骤进行操作;
遍历图块在SKTileMap中的位置。
找到彼此相邻的图块。
对于每组相邻的图块,收集:
绘制一个正方形,然后移至下一组图块,直到用完图块坐标为止。
Screenshot without visuals for comparison