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()
}
我正在使用 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()
}