Swift 4无限跑酷游戏如何实现障碍物特定侧面碰撞检测?
嘿,作为iOS开发新手能做出无限跑酷游戏已经超棒啦!针对你现在遇到的「玩家碰障碍物任意侧面都会死亡」的问题,我来给你几个实用的解决思路,都是实际开发中常用的方案~
核心思路:区分碰撞的位置/区域
当前代码只是检测了「玩家和障碍物发生碰撞」这个事件,但没有判断碰撞发生在障碍物的哪个部位。我们需要通过碰撞点位置、多物理体分区或者碰撞方向向量来精准限制触发死亡的区域。
方法1:通过碰撞接触点的位置判断(最适合新手)
在碰撞回调里获取接触点,把它转换成障碍物坐标系下的位置,再判断这个点是否在你想要触发死亡的区域内(比如障碍物的正面)。
修改后的代码示例:
func didBegin(_ contact: SKPhysicsContact) { var firstBody = contact.bodyA var secondBody = contact.bodyB // 统一把玩家设为firstBody,方便后续判断 if contact.bodyB.node?.name == "Player" { swap(&firstBody, &secondBody) } // 处理地面碰撞逻辑 if firstBody.node?.name == "Player" && secondBody.node?.name == "Ground" { canJump = true return } // 检查是否是玩家和障碍物/巴士的碰撞 guard firstBody.node?.name == "Player", let obstacleNode = secondBody.node, ["Bus", "Obstacle"].contains(obstacleNode.name) else { return } // 将碰撞点转换到障碍物的本地坐标系 let contactPoint = contact.contactPoint let localPoint = obstacleNode.convert(contactPoint, from: obstacleNode.scene!) // 假设我们只在玩家碰撞到障碍物的「前20%区域」时触发死亡 // 这里的数值需要根据你的障碍物尺寸、游戏方向调整 // 比如跑酷里障碍物通常从右往左移动,那左侧区域就是正面 let obstacleWidth = obstacleNode.frame.width if localPoint.x < obstacleWidth * 0.2 { playerDied() } }
小贴士:如果你的障碍物是从左往右移动,就把判断条件改成
localPoint.x > obstacleWidth * 0.8,对应障碍物的右侧区域。
方法2:给障碍物添加多个分区物理体
给障碍物的不同侧面单独设置物理体,分配不同的碰撞分类,只在玩家碰撞到「死亡区域」的物理体时触发逻辑。
步骤1:先定义物理分类常量
struct PhysicsCategory { static let Player: UInt32 = 0b1 << 0 static let Ground: UInt32 = 0b1 << 1 static let ObstacleDeathZone: UInt32 = 0b1 << 2 // 触发死亡的区域 static let ObstacleSafeZone: UInt32 = 0b1 << 3 // 安全区域 }
步骤2:给障碍物设置多物理体
// 假设你的障碍物是SKSpriteNode let obstacle = SKSpriteNode(imageNamed: "obstacle") obstacle.name = "Obstacle" // 创建死亡区域物理体(比如左侧20%宽度) let deathBody = SKPhysicsBody(rectangleOf: CGSize(width: obstacle.frame.width * 0.2, height: obstacle.frame.height)) deathBody.categoryBitMask = PhysicsCategory.ObstacleDeathZone deathBody.position = CGPoint(x: -obstacle.frame.width * 0.4, y: 0) // 调整位置到障碍物左侧 // 创建安全区域物理体 let safeBody = SKPhysicsBody(rectangleOf: CGSize(width: obstacle.frame.width * 0.8, height: obstacle.frame.height)) safeBody.categoryBitMask = PhysicsCategory.ObstacleSafeZone safeBody.position = CGPoint(x: obstacle.frame.width * 0.3, y: 0) // 合并两个物理体作为障碍物的整体物理体 obstacle.physicsBody = SKPhysicsBody(bodies: [deathBody, safeBody]) obstacle.physicsBody?.contactTestBitMask = PhysicsCategory.Player obstacle.physicsBody?.collisionBitMask = PhysicsCategory.Player | PhysicsCategory.Ground
步骤3:在碰撞回调里判断分类
func didBegin(_ contact: SKPhysicsContact) { var firstBody = contact.bodyA var secondBody = contact.bodyB if contact.bodyB.node?.name == "Player" { swap(&firstBody, &secondBody) } if firstBody.node?.name == "Player" { if secondBody.node?.name == "Ground" { canJump = true } else if secondBody.categoryBitMask == PhysicsCategory.ObstacleDeathZone { playerDied() } } }
这个方法适合障碍物形状固定的场景,物理引擎会帮你自动区分碰撞区域,不需要手动计算位置。
方法3:利用碰撞方向向量判断
通过SKPhysicsContact的contactNormal属性获取碰撞的方向向量,判断碰撞是否来自你想要的方向(比如玩家正面撞到障碍物)。
示例代码:
func didBegin(_ contact: SKPhysicsContact) { var firstBody = contact.bodyA var secondBody = contact.bodyB if contact.bodyB.node?.name == "Player" { swap(&firstBody, &secondBody) } // 处理地面碰撞 if firstBody.node?.name == "Player" && secondBody.node?.name == "Ground" { canJump = true return } guard firstBody.node?.name == "Player", ["Bus", "Obstacle"].contains(secondBody.node?.name) else { return } // contactNormal是碰撞的法向量,指向碰撞体的外部 // 假设游戏中玩家向右跑,障碍物向左移动,那么正面碰撞的法向量x分量会是负数 if contact.contactNormal.x < -0.5 { // 接近向左的方向,说明撞到障碍物正面 playerDied() } }
这个方法适合移动的障碍物,不需要关心物体尺寸,但需要调试法向量的数值,确保方向判断准确。
内容的提问来源于stack exchange,提问作者Louie Kotler
相关产品推荐
相关产品推荐

