如何在 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】:您可以做的是使用二进制文字:0b1
、0b10
、0b100
等。
但是,在 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 设备方向改变导致 SKLabelNode 变形 Swift
如何访问 SpriteKit 场景编辑器参考节点上自定义类中包含的 Swift 中的变量?
touchesBegan Swift 2 自定义 SpriteKit 节点