Как определить перечисление битовой маски категории для SpriteKit в Swift?
Чтобы определить перечисление битовой маски категории в Objective-C, я использовал:
typedef NS_OPTIONS(NSUInteger, CollisionCategory)
{
CollisionCategoryPlayerSpaceship = 0,
CollisionCategoryEnemySpaceship = 1 << 0,
CollisionCategoryChickenSpaceship = 1 << 1,
};
Как я могу добиться того же, используя Swift
? Я экспериментировал с перечислениями, но не могу заставить его работать. Вот что я попробовал до сих пор.
8 ответов
Что вы можете сделать, это использовать двоичные литералы: 0b1
, 0b10
, 0b100
, так далее.
Однако в Swift вы не можете использовать побитовые ИЛИ перечисления, поэтому в действительности нет смысла использовать битовые маски в перечислениях. Проверьте этот вопрос для замены NS_OPTION.
Если вы посмотрите этот быстрый учебник, вы можете избежать всего преобразования 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
Взгляните на игру AdvertureBuilding SpriteKit. Они перестроили его в Swift, и вы можете скачать исходный код на сайте разработчиков iOS8.
Они используют следующий метод создания enum:
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
}
}
Как отметил пользователь 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()
Простой способ обработки битовых масок в swift - создать перечисление типа UInt32, содержащее все ваши различные типы коллизий. То есть
enum ColliderType: UInt32 {
case Player = 1
case Attacker = 2
}
А затем в вашем классе игрока добавьте физическое тело и настройте обнаружение столкновений.
physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(size.width, size.height))
physicsBody.categoryBitMask = ColliderType.Player.toRaw()
physicsBody.contactTestBitMask = ColliderType.Attacker.toRaw()
physicsBody.collisionBitMask = ColliderType.Attacker.toRaw()
А для вашего класса атакующего (или снаряда, птицы, метеора и т. Д.) Установите его физическое тело как
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")
}
}
}
Добавив больше типов ColliderTypes, вы сможете обнаружить больше столкновений в вашей игре.
Попробуйте привести ваши дела как UInt.
enum CollisionCategory: UInt{
case PlayerSpaceship = 0
case EnemySpaceship = UInt(1 << 0)
case PlayerMissile = UInt(1 << 1)
case EnemyMissile = UInt(1 << 2)
}
Это избавляет меня от ошибок.
Я предпочитаю использовать как ниже, который работает просто отлично, и я думаю, что это самый близкий путь к вашей первоначальной попытке:
// MARK: Categories - UInt32
let playerCategory:UInt32 = 0x1 << 0
let obstacleCategory:UInt32 = 0x1 << 1
let powerUpCategory:UInt32 = 0x1 << 2
PS: это Swift 4
Есть небольшая ошибка с UInt, но, учитывая, что я думаю, что в любом случае используются только 32 бита, это будет работать. Я бы также предложил представить радар, вы должны иметь возможность использовать любое постоянное значение (1 << 2 всегда будет одинаковым)
Во всяком случае, вот как только они избавились от ошибок с UInts, это будет работать
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 с перечислением:
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