Creating a Subclass of Skshapenode

Creating a subclass of SKShapeNode

Try changing your initializer like this:

class ColorRectangle: SKShapeNode {

var width: CGFloat!
var height: CGFloat!
var rectColor: UIColor!

convenience init(rectOfSize: CGSize, colorOfRectangle rectColor: UIColor) {

self.init()

self.init(rectOfSize: rectOfSize)

self.rectColor = rectColor

self.fillColor = rectColor
self.strokeColor = rectColor
}

}

no designated init for SKShapeNode(circleOfRadius: radius)

how's this?

class Player: SKShapeNode {

init(circleOfRadius: CGFloat){
super.init()

let diameter = circleOfRadius * 2
self.path = CGPathCreateWithEllipseInRect(CGRect(origin: CGPointZero, size: CGSize(width: diameter, height: diameter)), nil)
}

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

}

Inherit SKShapeNode

SKShapeNode.init(circleOfRadius:) is a convenience initializer on SKShapeNode so you can't call it from a Swift initializer. Swift enforces the designated initializer pattern more strictly than Objective C does.

Unfortunately, it appears the designated initializer for SKShapeNode is just init, so you'll need to do something like this:

public class Player: SKShapeNode {
public var playerName : String
private var inventory: [enumObject]

init(nameOfPlayer:String, position:CGPoint, radius: CGFloat) {
playerName = nameOfPlayer
inventory = [enumObject]()

super.init()

self.path = CGPath(ellipseIn: CGRect(origin: .zero, size: CGSize(width: radius, height: radius)), transform: nil)

self.position = position
self.fillColor = SKColor.white
}

// init?(coder:) is the other designated initializer that we have to support
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

The code above works for subclassing SKShapeNode, but given the API that Apple provides and considering how your code might need to change in the future, it might make more sense to create an SKNode subclass that contains one or more SKShapeNodes. In this setup, if you wanted to represent the player as more than just a simple circle, you could simply add additional nodes to the player node.

Subclassing SKShapeNode with Swift

You have two problems with the code you tried:

  1. rectOfSize in SKShapeNode takes a CGSize not a CGPoint
  2. rectOfSize in SKShapeNode is a convenience initializer so you won't be able to call it from a subclass. You will have to call super.init() and implement the rect functionality yourself

You can do something like this:

init(rectOfSize: CGSize) {
super.init()

var rect = CGRect(origin: CGPointZero, size: rectOfSize)
self.path = CGPathCreateWithRect(rect, nil)
}

Why is my SKShapeNode subclass not appearing when I run my app?

I couldn't figure out a solution when the class inherits from an SKShapeNode; however, I was able to figure out how to do it when it inherits from an SKNode. All that is required is to initialize the SKNode and then add an SKShapeNode - with your desired attributes - as a child of the SKNode like what is done below:

class FallingBlock: SKNode {

init(side: CGPoint) {
super.init()
self.addNewBlock(side)
}

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

deinit {
print("falling block reference deleted")
}

private func addNewBlock(_ side: CGPoint) {
var color = [UIColor.red,UIColor.cyan,UIColor.green,UIColor.yellow,UIColor.orange,UIColor.systemPink].randomElement() as! UIColor

let obstacle = SKShapeNode(rectOf: CGSize(width: 20, height: 20), cornerRadius: 3)
obstacle.fillColor = color
obstacle.strokeColor = color
obstacle.position = side
obstacle.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 20, height: 20))
obstacle.physicsBody?.affectedByGravity = true
obstacle.zPosition = 1

obstacle.physicsBody?.categoryBitMask = PhysicsCategories.obstacleCategory
obstacle.name = ObjectNames.obstacle
self.addChild(obstacle)
}
}

why cant use subclass of SKShapenode?

You are not properly subclassing, You are using your Enemy object as both a factory and an instance, separate the 2.

Factory:

public class EnemyFactory
{
public func createEnemy(color: SKColor, radius: CGFloat, position: CGPoint) -> Enemy{
let enemy = Enemy(circleOfRadius: radius)
enemy.setup(color:color, radius: radius, position: position)
return enemy
}
}

Instance Class:

public class Enemy: SKShapeNode {
static let Factory = EnemyFactory()
public var health: Int = 5

func setup (color: SKColor, radius: CGFloat, position: CGPoint) {
self.position = position
fillColor = color
glowWidth = 0.5
self.health = 5
strokeColor = color
let physicsBody = SKPhysicsBody(circleOfRadius: radius)
physicsBody.affectedByGravity = false
physicsBody.isDynamic = true
physicsBody.collisionBitMask = GameScene.bodyType.player.rawValue
physicsBody.categoryBitMask = GameScene.bodyType.enemy.rawValue
physicsBody.contactTestBitMask = GameScene.bodyType.player.rawValue
physicsBody.usesPreciseCollisionDetection = true
self.physicsBody = physicsBody
}
override init(){
super.init()

}

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

Some liberties I took: I removed local redundant variables like color and scene, you can grab these straight from the object, no need to have another variable to save them. You also do not need getters and setters, Swift does this for you already.

Setting non-optional properties in an SKShapeNode subclass

You can not call the method addChild before calling the super.init so here is the fix,

class MyNode: SKShapeNode {
private var label: SKLabelNode

init(_ frame: CGRect) {
self.label = SKLabelNode(text: "Some text")
super.init()

self.path = CGPath.init(rect: frame, transform: nil)

self.addChild(self.label)
}

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


Related Topics



Leave a reply



Submit