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

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

typedef NS_OPTIONS(NSUInteger, CollisionCategory)
{
    CollisionCategoryPlayerSpaceship = 0,
    CollisionCategoryEnemySpaceship = 1 << 0,
    CollisionCategoryChickenSpaceship = 1 << 1,
};

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

【问题讨论】:

    标签: ios objective-c swift sprite-kit bitmask


    【解决方案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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多