My code works fine! But, the more I study about Abstract classes and Protocols the more I start to think that the "logic" behind my code is totally wrong. Let me explain you more detailed:
I have a class
named SpaceObject
which is a kind of abstraction for the inherited classes. It basically hold some properties and physics. This class should not be initialized since it works just as "blueprint"
class SpaceObject: SKSpriteNode {
final var atmosphere: SKSpriteNode
init(style: SKTexture, initialVelocity: CGVector) {
atmosphere = SKSpriteNode(texture: SKTexture(imageNamed: "atmosphere"), size: CGSize(width: 100, height: 100))
atmosphere.zPosition = 2
// calls the initializer
super.init(texture: style, color: .clear, size: style.size())
physicsBody = SKPhysicsBody(circleOfRadius: style.size().width)
physicsBody?.velocity = initialVelocity
physicsBody?.affectedByGravity = false
...
addChild(atmosphere)
}
final func cleanAtmosphere() {
atmosphere.run( ... )
}
}
Here I have the subclasses. They just "fill" the SpaceObject class but use its physics and some other functions (such as cleanAtmosphere()
).
final class World: SpaceObject {
enum WorldType: String {
case Venus, Earth, ...
}
var type: WorldType
var gravity: CGFloat
init(world: WorldType, initialVelocity: CGVector, gravity: CGFloat) {
self.type = world
self.gravity = gravity
// calls the initializer
super.init(style: SKTexture(imageNamed: "world_\(self.type)"), initialVelocity: initialVelocity)
}
func touchedOccurred() {
...
cleanAtmosphere()
...
}
}
final class Star: SpaceObject {
enum StarType: String {
case Sirius, Polaris, ...
}
var type: StarType
init(star: StarType, initialVelocity: CGVector) {
self.type = star
// calls the initializer
super.init(style: SKTexture(imageNamed: "star_\(self.type)"), initialVelocity: initialVelocity)
}
func explode() {
...
}
}
What I do for creating an object is just:
let newWorld = World(world: .Venus, initialVelocity: someVector, gravity: CGFloat(8.87))
and I'm sure it will have an atmosphere SKSpriteNode
and some physics rules which are valid for each SpaceObject.
But... I'm not sure I'm doing it correctly, I read that protocols need to handle situations like this, but I still can't figure out how. Any hint?
You can use inheritance to do what you want, but protocols are also an option. The advantages of protocols are that Swift structs can use them and a class can conform to multiple protocols but can inherit from only one class.
You mentioned physics rules. If you wanted to use a protocol, you would create a protocol for the rules and include the functions that classes/structs have to implement to conform to the protocol. That function would create the rule.
protocol PhysicsRules {
// Add any arguments the function takes.
func yourPhysicsRule()
// Add any other physics functions you want here.
}
Then your World and Star classes conform to the protocol and implement the yourPhysicsRule
function.
final class World: SpaceObject, PhysicsRules {
func yourPhysicsRule() {
// Add the code to create the rule here.
}
}
You could come up with better names for the protocol and the function, but this gives you an idea of what you have to do to transition to a protocol.
Doing a web search for Swift protocols
provides many tutorials on protocol-oriented programming, including the following:
A Beginner’s Guide to Protocols and Protocol Extensions in Swift
Protocol-Oriented Programming Tutorial in Swift 5.1: Getting Started