SKScene - 如何从 SKSpriteNode 获取类

Posted

技术标签:

【中文标题】SKScene - 如何从 SKSpriteNode 获取类【英文标题】:SKScene - How to Get Class from SKSpriteNode 【发布时间】:2020-01-23 06:38:42 【问题描述】:

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

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 关联的类......?有什么方法可以让我的类脚本成为 SKSpriteNode 的一部分,如果是这样 - 我如何获取附加的脚本来调用它的函数?

这也是我的子弹类

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!;
    

【问题讨论】:

尝试将第一个主体投射为子弹类型 刚刚试过-让 tempBullet = ((firstBody as? Bullet));它编译,但打印 nil... 让你成为 SKSpriteNode 类型的子弹类,然后通过铸造希望它不会打印 nil 我正在这样做... class Bullet : SKSpriteNode 但它仍然为零...我添加它的方式是否正确... self.addChild()...也会将该函数添加到问题中以供参考 刚刚添加了更多代码/细节以供参考@SanadBarjawi 【参考方案1】:

不确定为什么要以这种方式创建项目符号。我认为它应该看起来更像。当子类化 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 时它为 nil 的原因是您在调用 newBullet.CreateBullet() 时创建了一个 SKSpriteNode 而不是 Bullet 类。

最后在physicsContact 中有两个物体在一个接触中。我们不知道哪个是敌人,哪个是子弹。所以我们可以为这两种情况编写代码,尽管如果您要检查更多的冲突,还有更好的方法,比如这个答案:Determining Contact Bodies

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!")
        
    


 

【讨论】:

【参考方案2】:

子类化 SKSpriteNode 完全没问题,也是推荐的方法。 无论如何,如果您出于任何原因做出了不同的设计选择以将 SKNode 与其处理程序分开,您可以使用 SKNode userData,旨在支持游戏逻辑而无需子类化节点。 所以一个通用的解决方案可以是以下一个(我是 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)
        
    

你的 Bullet 类是

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()


【讨论】:

以上是关于SKScene - 如何从 SKSpriteNode 获取类的主要内容,如果未能解决你的问题,请参考以下文章

如何从 SKScene 到 UIViewController?

SKScene - 如何从 SKSpriteNode 获取类

如何从 SKScene 中执行 segue?

如何从 SKScene 内部获得触摸时的像素颜色?

如何从 Swift SpriteKit 中的另一个 SKScene 访问变量

如何将动作从 SKScene 组件传播到 UIViewController