SKScene - 如何从 SKSpriteNode 获取 Class

SKScene - How to Get Class from SKSpriteNode

我正在使用 spritekit 创建场景。我有 3 个 classes:GameScene、Bullet 和 Enemy。在我的 GameScene 中,我添加了通过 Bullet 和 Enemy 创建 SKSpriteNode classes.

var newBullet = Bullet(); 
newBullet.CreateBullet()
self.addChild(newBullet.GetSpriteNode())

稍后我将通过扩展 SKPhysicsContactDelegate 使用碰撞检测。

func didBegin(_ contact: SKPhysicsContact) {
    print("collision detected!")
    let firstBody = contact.bodyA.node as! SKSpriteNode;
    let secondBody = contact.bodyB.node as! SKSpriteNode;

    //here's where I'd like to call bullet.hit() 
    //var bullet = firstBody.GetClass(Bullet)
    //bullet.hit()
    // I tried -  let tempBullet = ((firstBody as? Bullet));
    //but firstBody as? Bullet returns nil
    //print(firstBody) - it indeed is my bullet sprite... why is it nil?
}

我在上面写了我的伪代码...我如何获得与 SKSpriteNode 关联的 class..?有没有什么办法可以让我的 class 脚本成为 SKSpriteNode 的一部分,如果是这样 - 我如何获得附加的脚本来调用它的函数?

还有我的要点class

class Bullet : SKSpriteNode{
   var skNode : SKSpriteNode?

   func CreateBullet(){
        skNode = SKSpriteNode(color: SKColor.white, size: CGSize(width: 2, height: 10))
        skNode?.name = "bullet"
        skNode?.position = CGPoint(x: 0, y: 0)

        skNode?.physicsBody = SKPhysicsBody(rectangleOf: skNode!.size)

        skNode?.physicsBody?.isDynamic = true;
        skNode?.physicsBody?.allowsRotation = false;
        skNode?.physicsBody?.pinned = false;
        skNode?.physicsBody?.affectedByGravity = false;

        skNode?.physicsBody?.categoryBitMask = PhysicsCategory.Bullet;
        skNode?.physicsBody?.collisionBitMask = PhysicsCategory.Enemy;
        skNode?.physicsBody?.contactTestBitMask = PhysicsCategory.Enemy;
    }

    func GetSKNode() -> SKNode{
        return skNode!;
    }
}

不确定为什么要按照您的方式创建子弹。我认为它应该看起来更像。当 subclassing 一个 SKSpriteNode 时,您应该覆盖初始化程序以添加您自己的自定义设置。

class Bullet: SKSpriteNode {

    init() {
        let size = CGSize(width: 2, height: 10)
        super.init(texture: nil, color: .white, size: size)
        self.name = "bullet"
        self._setupBody(size: size)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    //Prefix private methods with an underscore
    private func _setupBody(size: CGSize) {
        self.physicsBody = SKPhysicsBody(rectangleOf: size)
        self.physicsBody?.isDynamic = true
        self.physicsBody?.allowsRotation = false
        self.physicsBody?.pinned = false
        self.physicsBody?.affectedByGravity = false
        self.physicsBody?.categoryBitMask = PhysicsCategory.Bullet
        self.physicsBody?.collisionBitMask = PhysicsCategory.Enemy
        self.physicsBody?.contactTestBitMask = PhysicsCategory.Enemy
    }

}

当您尝试投射为 Bullet 时它为零的原因是您在调用 newBullet.CreateBullet() 而不是实例时创建了 SKSpriteNode 子弹 class.

最后在 physicsContact 中有两个 body 在接触。我们不知道哪个是敌人,哪个是子弹。所以我们可以为这两种情况编写代码,但如果您要检查更多碰撞,则有更好的方法,例如这个答案:

class BulletScene:SKScene, SKPhysicsContactDelegate {


    func didBegin(_ contact: SKPhysicsContact) {

        //Case #1: Bullet = bodyA and Enemy = bodyB
        if let bullet = contact.bodyA.node as? Bullet, let enemy = contact.bodyB as? Enemy {
            print("Bullet was BodyA!")
        //Case #2: Bullet = bodyB and Enemy = bodyA
        }else if let enemy = contact.bodyA as? Enemy, let bullet = contact.bodyB.node as? Bullet {
            print("Bullet was BodyB!")
        }
    }


} 

Subclass使用 SKSpriteNode 完全没问题,也是推荐的方式。
无论如何,如果您出于任何原因做出不同的设计选择以将 SKNode 设置与其处理程序分开,您可以使用 SKNode userData,旨在支持游戏逻辑而无需子 classing 节点。
所以一个通用的解决方案可以是以下一个(我是Swift的新手,也许代码可以写得更好)。
一劳永逸,将此代码复制到您的项目中:

protocol SKNodeHandler: class { }
extension SKNode {
    var handler : SKNodeHandler {
        set {
            if self.userData == nil {
                self.userData = NSMutableDictionary()
            }
            self.userData![0xFF] = NSValue.init(nonretainedObject: newValue)
        }
        get {
            let nsValue = self.userData![0xFF] as? NSValue
            return (nsValue?.nonretainedObjectValue as! SKNodeHandler)
        }
    }
}

你的子弹class是

class Bullet : SKNodeHandler {
   var skNode : SKSpriteNode?

   func CreateBullet() {
       skNode = SKSpriteNode(/* ... */)
       skNode.handler = self
   }
   /* ... */
   
   func hit() {
       skNode.run( /* ... */ )
   }

}

并且在接触检测中:

func didBegin(_ contact: SKPhysicsContact) {
    //You identified bodyA is the Bullet node (by its category bit mask or its node name)
    let bullet = contact.bodyA.node.handler as? Bullet
    bullet?.hit()

}