简体   繁体   English

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

[英]How to define category bit mask enumeration for SpriteKit in Swift?

To define a category bit mask enum in Objective-C I used to type:要在 Objective-C 中定义类别位掩码枚举,我曾经键入:

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

How can I achieve the same using Swift ?如何使用Swift实现相同的目标? I experimented with enums but can't get it working.我尝试了枚举,但无法使其正常工作。 Here is what I tried so far.这是我到目前为止所尝试的。

错误截图

What you could do is use the binary literals: 0b1 , 0b10 , 0b100 , etc.你可以做的是使用二进制文字: 0b10b100b100 ,等等。

However, in Swift you cannot bitwise-OR enums, so there is really no point in using bitmasks in enums.然而,在 Swift 中你不能按位或枚举,所以在枚举中使用位掩码真的没有意义。 Check out this question for a replacement for NS_OPTION.查看此问题以替代 NS_OPTION。

If you look at this swift tutorial , you can avoid the whole toRaw() or rawValue conversion by using:如果您查看此 swift 教程,您可以使用以下方法避免整个 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 

Take a look at the AdvertureBuilding SpriteKit game.看看AdvertureBuilding SpriteKit 游戏。 They rebuilt it in Swift and you can download the source on the iOS8 dev site.他们在 Swift 中重新构建了它,您可以在 iOS8 开发站点上下载源代码。

They are using the following method of creating an enum:他们使用以下创建枚举的方法:

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

And the setup is like this设置是这样的

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

And check like this:并像这样检查:

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
    }
}

As noted by, user949350 you can use literal values instead.正如 user949350 所指出的,您可以改用文字值。 But what he forgot to point out is that your raw value should be in "squares".但是他忘记指出的是,您的原始值应该是“平方”。 Notice how the sample of code of Apple enumerates the categories.请注意 Apple 的代码示例是如何枚举类别的。 They are 1, 2, 4, 8 and 16, instead of the usual 1, 2, 3, 4 , 5 etc.它们是 1、2、4、8 和 16,而不是通常的 1、2、3、4、5 等。

So in your code it should be something like this:所以在你的代码中它应该是这样的:

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

} }

And if you want your player node to collide with either enemy or chicken spaceship, for example, you can do something like this:例如,如果您希望玩家节点与敌人或小鸡飞船发生碰撞,您可以执行以下操作:

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

Try casting your cases as UInt.尝试将您的案例转换为 UInt。

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

This gets rid of the errors for me.这为我消除了错误。

An easy way to handle the bitmasks in swift is to create an enum of type UInt32 containing all your different collision types.在 swift 中处理位掩码的一种简单方法是创建一个 UInt32 类型的枚举,其中包含所有不同的碰撞类型。 That is那是

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

And then in your Player Class add a physics body and setup the collision detection然后在您的播放器类中添加一个物理体并设置碰撞检测

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

And for your Attacker Class (or projectile, bird, meteor, etc.) setup its physics body as并且对于您的攻击者类(或射弹、鸟、流星等),将其物理体设置为

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

(Note that you can setup the physics body to be whatever shape you want) (请注意,您可以将物理体设置为您想要的任何形状)

Then make sure you have a SKPhysicsContactDelegate setup (eg you can let your scene be the delegate) and then implement the optional protocol method didBeginContact然后确保你有一个SKPhysicsContactDelegate设置(例如你可以让你的场景成为didBeginContact )然后实现可选的协议方法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")
        }

    }

}

By adding more ColliderTypes you can detect more collisions in your game.通过添加更多 ColliderTypes,您可以检测游戏中的更多碰撞。

There is a bit of a bug with UInt, but given I think only 32 bits are used anyway this would work. UInt 有一些错误,但鉴于我认为无论如何都只使用了 32 位,这会起作用。 I would also suggest submitting a radar, you should be able to use any constant value (1 << 2 will always be the same)我还建议提交雷达,您应该可以使用任何常量值(1 << 2 将始终相同)

Anyway, here's once they've got rid of the bugs with UInts, this would work无论如何,一旦他们摆脱了 UInts 的错误,这将起作用

enum CollisionCategory: Int{ case PlayerSpaceship = 0, EnemySpaceShip, PlayerMissile, EnemyMissile enum 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()

Swift 3 with enum:带有枚举的 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 

I prefer to use like below which works just fine and I think it is the closest way to your original attempt:我更喜欢使用下面这样的方法,它工作得很好,我认为这是最接近您最初尝试的方法:

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

PS: This is Swift 4 PS:这是 Swift 4

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM