7

我正在学习 Swift,作为一个项目,我正在开发一款基于瓷砖的 2D 游戏,类似于超级马里奥,我的角色将在瓷砖上行走和跳跃。

最新版本的 Xcode 和 Sprite Kit 提供了直接在 Xcode 中创建 Tile Map 的能力。

在介绍新的 Xcode 和 Sprite 套件时,这个人演示了一个类似于我正在开发的游戏。

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

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

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

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

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

4

4 回答 4

6

Apple 工作人员 Bobjt在这里表示,“正确的方法”是将用户数据添加到您的SKTileDefinition对象中,并使用它来识别物理实体并将其添加到您的图块中。

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

在此处输入图像描述

然后在代码中,您将检查每个图块定义以查看它是否具有用户数据值。如果是这样,那么您需要计算 tile 的位置并在新节点上添加一个物理体,将其作为 tile map 的父级。这是 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 增强瓦片地图 API,以便我们可以将物理实体直接添加到单个瓦片中。也许在此过程中,他们可以优化引擎,以便自动合并单个图块上的物理实体以形成更大、更优化的形状,从而提高系统性能。

更新:我就这个问题向 Apple提出了请求,不管它有什么好处

于 2018-03-15T01:14:16.163 回答
2

或者,您可以对要赋予 SKPhysicsBody 的图块应用线扫描算法。

您可以分四个步骤完成此操作;

  1. 遍历 SKTileMap 中瓦片的位置。

  2. 找到彼此相邻的瓷砖。

  3. 对于每组相邻的瓷砖,收集:

    • 左下角坐标和
    • 一个直角坐标
  4. 画一个正方形,然后移动到下一组瓷砖,直到你用完瓷砖坐标。

没有用于比较的视觉效果的屏幕截图

没有显示物理体的视觉效果的屏幕截图

在类似的帖子中查看我的回答,了解如何实现这一点。

于 2019-06-14T19:14:58.387 回答
2

我不确定是否有一种万无一失的方法可以做到这一点......但这里有两种方法可以考虑如何尝试将物理应用于瓷砖地图。

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

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

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

在此处输入图像描述

从 Apple 的 WWDC 中提取的关于瓷砖和物理的成绩单:

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

我通过利用我们可以放在每个图块上的自定义用户数据来实现这一点。

在这里,我将在我们的瓷砖组中向您展示。

在此处选择一种变体。

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

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

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

每当我找到一个时,我都会创建一些物理数据以允许玩家与之碰撞。

因为它只是在我们的瓦片地图中,假设我想在这里越过这堵大墙。

当我运行游戏时,你会发现我的家伙实际上无法跳得足够高以克服它。

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

我真的很想推动那个东西。

所以,由于我们只是从我们的瓦片和我们的用户数据中生成我们的物理数据,我们所能做的就是去这里并擦除这些瓦片并再次构建和运行我们的游戏。

瓷砖现在不见了,我们现在可以穿过那里。

而且我们不必更改代码或任何东西。

我们只是使用从瓦片地图中提取的数据来设置瓦片。

就是这么简单。


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

于 2016-10-04T18:35:24.463 回答
1

我花了两天时间尝试不同的想法,但找不到比我在其他答案中发布的更好的了。正如那里提到的,这是 Apple 工作人员推荐的方式,据我所知,这是让 SpriteKit 自动将物理实体添加到所有图块的最有效方式。我已经对其进行了测试,并且可以正常工作。(虽然我仍然屏住呼吸让苹果公司添加一种将物理实体放在瓷砖上的直接方式)。

但还有其他考虑。如果您因为场景中的物理实体过多而遇到性能问题,您可能需要尝试其中一种其他方法。但是,它们都比上述方法更耗时。唯一可以证明使用这些劳动密集型方法之一是合理的原因是,如果您由于性能问题需要减少场景中物理实体的数量。否则,我认为上面提到的“自动”方法是我们拥有的最佳选择。

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

替代方法 #1 - 使用更少的物理实体

在编辑器中创建您的瓦片地图。然后继续在编辑器中工作,将颜色精灵 ( SKSpriteNodes) 拖到需要物理实体的地图部分上。对节点进行整形,为需要物理体的区域制作最大的矩形。这最适用于大而平坦的表面,如墙壁、地板、天花板、平台、板条箱等。这很乏味,但与使用上述自动方法相比,您最终在模拟中得到的物理实体要少得多。

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

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

一旦你的屏障瓷砖被充分识别,编写代码来检查玩家精灵当前占据的瓷砖的用户数据值,并相应地限制精灵的移动。例如,如果玩家输入一个标有上限的标题,您的移动代码将不允许玩家精灵向上移动。同样,如果玩家进入标记最左侧边界的图块,您的移动代码将不会让玩家向左移动。

于 2018-03-16T11:10:05.547 回答