如何在 Swift 中为 SpriteKit 定义类别位掩码枚举?

Posted

技术标签:

【中文标题】如何在 Swift 中为 SpriteKit 定义类别位掩码枚举?【英文标题】:How to define category bit mask enumeration for SpriteKit in Swift? 【发布时间】:2014-07-27 00:11:33 【问题描述】:

为了在 Objective-C 中定义一个类别位掩码枚举,我曾经键入:

typedef NS_OPTIONS(NSUInteger, CollisionCategory)

    CollisionCategoryPlayerSpaceship = 0,
    CollisionCategoryEnemySpaceship = 1 << 0,
    CollisionCategoryChickenSpaceship = 1 << 1,
;

我怎样才能使用Swift 达到同样的效果?我尝试了枚举,但无法正常工作。这是我到目前为止所尝试的。

【问题讨论】:

【参考方案1】:

您可以做的是使用二进制文字:0b10b100b100 等。

但是,在 Swift 中,您不能对枚举进行按位或运算,因此在枚举中使用位掩码确实没有意义。查看 this question 以替换 NS_OPTION。

【讨论】:

正如其他答案所暗示的,只要您使用原始值,您就可以使用枚举。 @Crashalot 原始值是什么样的?对不起,只是不知道。我有点认为它们可能是 0b0001,但显然不是。 @Confused - rawValue 是枚举的“属性”,它返回用于表示枚举情况的实际原始值。如果您要求枚举的特定情况,它不会返回实际值,而是返回分配给它的类别。因此,例如给定一个枚举“Fruit”,其大小写为“banana”,设置为 0b100,如果您打印出“Fruit.banana”的值,它将返回“banana”,但如果您打印“Fruit.banana”的值。 banana.rawValue" 它将打印实际值,4. 谢谢@daver。我必须对此进行深思熟虑,然后将其画出几次,然后尝试想象我要画的东西……也许这对我来说是有意义的。设计师的大脑在编程范式和流程的模糊性、不透明性和纯粹任意性方面存在巨大的问题。 @confused - 不用担心,我认为要理解的关键思想是 Swift 枚举正在努力让您将它们视为类别,即只是名称而不是数字或值,并且仅当你真的,真的,真的很想知道下面存储的“真实价值”,然后 Swift 会强制你使用 .rawValue 来查看。【参考方案2】:

如果您查看this swift tutorial,您可以使用以下方法避免整个 toRaw() 或 rawValue 转换:

struct PhysicsCategory 
  static let None      : UInt32 = 0
  static let All       : UInt32 = UInt32.max
  static let Monster   : UInt32 = 0b1       // 1
  static let Projectile: UInt32 = 0b10      // 2


monster.physicsBody?.categoryBitMask = PhysicsCategory.Monster 
monster.physicsBody?.contactTestBitMask = PhysicsCategory.Projectile 
monster.physicsBody?.collisionBitMask = PhysicsCategory.None 

【讨论】:

【参考方案3】:

看看 AdvertureBuilding SpriteKit 游戏。他们用 Swift 重建了它,你可以在 ios8 开发网站上下载源代码。

他们使用以下方法创建枚举:

enum ColliderType: UInt32 
  case Hero = 1
  case GoblinOrBoss = 2
  case Projectile = 4
  case Wall = 8
  case Cave = 16

设置是这样的

physicsBody.categoryBitMask = ColliderType.Cave.toRaw()
physicsBody.collisionBitMask = ColliderType.Projectile.toRaw() | ColliderType.Hero.toRaw()
physicsBody.contactTestBitMask = ColliderType.Projectile.toRaw()

然后像这样检查:

func didBeginContact(contact: SKPhysicsContact) 

// Check for Projectile
    if contact.bodyA.categoryBitMask & 4 > 0 || contact.bodyB.categoryBitMask & 4 > 0   
          let projectile = (contact.bodyA.categoryBitMask & 4) > 0 ? contact.bodyA.node : contact.bodyB.node
    

【讨论】:

【参考方案4】:

如 user949350 所述,您可以改用文字值。但他忘记指出的是,你的原始价值应该是“正方形”。请注意 Apple 的代码示例如何枚举类别。它们是 1、2、4、8 和 16,而不是通常的 1、2、3、4、5 等。

所以在你的代码中应该是这样的:

enum CollisionCategory:UInt32 
case PlayerSpaceShip = 1,
case EnemySpaceShip = 2,
case ChickenSpaceShip = 4,

如果你想让你的玩家节点与敌人或鸡飞船发生碰撞,例如,你可以这样做:

playerNode.physicsBody.collisionBitMask = CollisionCategory.EnemySpaceShip.toRaw() | CollisionCategory.ChickenSpaceShip.toRaw()

【讨论】:

【参考方案5】:

尝试将您的案例转换为 UInt。

enum CollisionCategory: UInt
    case PlayerSpaceship = 0
    case EnemySpaceship = UInt(1 << 0)
    case PlayerMissile = UInt(1 << 1)
    case EnemyMissile = UInt(1 << 2)

这为我消除了错误。

【讨论】:

我复制了您的代码,但仍然遇到与屏幕截图中相同的错误。它没有改变任何东西。 有趣。那么你总是可以默认只使用文字 1,2,4,8 等【参考方案6】:

在 swift 中处理位掩码的一种简单方法是创建一个 UInt32 类型的枚举,其中包含所有不同的碰撞类型。那是

enum ColliderType: UInt32 
    case Player = 1
    case Attacker = 2

然后在你的Player Class中添加一个物理体并设置碰撞检测

physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(size.width, size.height))
physicsBody.categoryBitMask = ColliderType.Player.toRaw()
physicsBody.contactTestBitMask = ColliderType.Attacker.toRaw()
physicsBody.collisionBitMask = ColliderType.Attacker.toRaw()

而对于您的Attacker Class(或弹丸、鸟、流星等),将其物理体设置为

physicsBody = SKPhysicsBody(circleOfRadius: size.width / 2)
physicsBody.categoryBitMask = ColliderType.Attacker.toRaw()
physicsBody.contactTestBitMask = ColliderType.Player.toRaw()
physicsBody.collisionBitMask = ColliderType.Player.toRaw()

(请注意,您可以将物理体设置为您想要的任何形状)

然后确保您有一个SKPhysicsContactDelegate 设置(例如,您可以让您的场景成为委托),然后实现 可选 协议方法didBeginContact

class GameScene: SKScene, SKPhysicsContactDelegate 

    override func didMoveToView(view: SKView) 

        physicsWorld.contactDelegate = self
        // Additional setup...

    

    func didBeginContact(contact: SKPhysicsContact!) 

        println("A collision was detected!")

        if (contact.bodyA.categoryBitMask == ColliderType.Player.toRaw() &&
            contact.bodyB.categoryBitMask == ColliderType.Attacker.toRaw()) 

            println("The collision was between the Player and the Attacker")
        

    


通过添加更多 ColliderType,您可以在游戏中检测到更多碰撞。

【讨论】:

枚举中的位掩码值必须是 2 的平方,对吧?如果是这样,也许为未来的读者强调这一点,他们假设您的示例中的下一个值可能是 3。【参考方案7】:

UInt 存在一些错误,但考虑到我认为无论如何只使用 32 位,这将起作用。我还建议提交雷达,您应该可以使用任何常量值(1

不管怎样,一旦他们摆脱了 UInts 的错误,这就行了

枚举 CollisionCategory: Int case PlayerSpaceship = 0, EnemySpaceShip, PlayerMissile, EnemyMissile

func collisionMask()->Int
    switch self
    case .PlayerSpaceship:
        return 0;
    default:
        return 1 << (self.toRaw()-1)
    


CollisionCategory.PlayerMissle.collisionMask()

【讨论】:

【参考方案8】:

带有枚举的 Swift 3:

enum PhysicsCategory: UInt32 
  case none = 1
  case monster = 2
  case projectile = 4
  case wall = 8


monster.physicsBody?.categoryBitMask = PhysicsCategory.monster.rawValue 
monster.physicsBody?.contactTestBitMask = PhysicsCategory.projectile.rawValue
monster.physicsBody?.collisionBitMask = PhysicsCategory.none.rawValue 

【讨论】:

【参考方案9】:

我更喜欢像下面这样使用它,它工作得很好,我认为这是最接近你最初尝试的方法:

// MARK: Categories - UInt32
let playerCategory:UInt32 = 0x1 << 0
let obstacleCategory:UInt32 = 0x1 << 1
let powerUpCategory:UInt32 = 0x1 << 2

P.S.:这是 Swift 4

【讨论】:

以上是关于如何在 Swift 中为 SpriteKit 定义类别位掩码枚举?的主要内容,如果未能解决你的问题,请参考以下文章

在 SpriteKit 中为弹跳球创建音效

SpriteKit 设备方向改变导致 SKLabelNode 变形 Swift

如何访问 SpriteKit 场景编辑器参考节点上自定义类中包含的 Swift 中的变量?

touchesBegan Swift 2 自定义 SpriteKit 节点

如何使用 Swift 在 SpriteKit 程序中初始化/移动相机?

如何使用 Swift 2(Xcode 7)在我的 Sprite Kit 游戏中为背景音乐设置静音按钮?