Create a subclass of a SKSpriteNode subclass

inheritance, objective-c, skspritenode, sprite-kit

Solution

The answer is less complex than you think. Well, the code might be a bit more complex, but once you have the structure it is most flexible. Creating the different types of spaceships will also be a lot more readable.

You can override the initializer method in the subclass. As a sidenote, use `(instancetype)` instead of `(id)` (source: instancetype @ NSHipster).

As you are adding custom body sprites to the object, I would opt to subclass SKNode instead of SKSpriteNode (so `@interface SpaceshipNode : SKNode` instead of `@interface SpaceshipNode : SKSpriteNode`).

@interface SpaceshipNode : SKNode
  @property SKColor * color; // Use SKColor instead of NSColor
  @property CGFloat engineThrust;
@end

// ... 

@implementation SpaceshipNode

- (instancetype) init {
  if (self == [super init]) {
    NSLog(@"A new SpaceshipNode was just init'ed.");
    // set some default initial values here that all brand-new SpaceshipNodes will inherit

    // perhaps create and add a basic body sprite
    // SKSpriteNode * body = ...;
    // [self addChild:body];

    // set thrust
    self.engineThrust = 2.0;
  }
  return self;
}

Then you can subclass and create a new type of spaceship. Awesome!

@interface DestroyerSpaceship : SpaceshipNode
  @property CGFloat missileThrust;
@end

@implementation DestroyerSpaceship

- (instancetype) init {
  // note that [super init] will call the SpaceshipNode's init method
  if (self = [super init]) {
    NSLog(@"A new DestroyerSpaceship was just init'ed.");

    // add a body sprite
    // SKSpriteNode * body = ...;
    // [self addChild:body];

    // a Destroyer is much faster than your average spaceship
    self.engineThrust = 10.0;

    // set class specific variables
    self.missileThrust = 5.f;
  }
  return self;
}

Now, you can just call:

SpaceshipNode * newSpaceShip = [SpaceshipNode new]; // short for [[SpaceshipNode alloc] init];
DestroyerSpaceship * newDestroyer = [DestroyerSpaceship new];

These two lines will log the following. The last two lines are caused by the Destroyer, which first calls the `SpaceshipNode` `init`, and then the Destroyer-specific `init` method.

A new SpaceshipNode was just init'ed.

A new SpaceshipNode was just init'ed.

A new DestroyerSpaceship was just init'ed.

And you can even use it like this:

SpaceshipNode * newUnidentifiedVessel = [DestroyerSpaceship new];

if ([newUnidentifiedVessel isKindOfClass:[DestroyerSpaceship class]]) {
  NSLog(@"We are under attack! Route power to shields!");
}

Problem

Let's say I want to create a bunch of different types of Spaceships. I want to setup a base spaceship class that I can use to create other spaceships with minor differences. My base class looks like this. ``` // BaseSpaceship.h @interface SpaceshipNode : SKSpriteNode @property NSColor color; @property CGFloat engineThrust; + (id)baseSpaceshipWithImageNamed:(NSString *)name; @end // BaseSpaceship.m @implementation BaseSpaceship + (id)baseSpaceshipWithImageNamed:(NSString *)name { BaseSpaceship *ship = [BaseSpaceship spriteNodeWithImageNamed:name]; ship.color = [NSColor redColor]; ship.engineThrust = 2.0; return ship; } @end ``` I can create a ship in MyScene.m like this just fine. ``` BaseSpaceship *baseClass = [BaseSpaceship baseSpaceshipWithImageNamed:@"BaseShip"]; ``` However, I'm not sure how to create a subclass of `BaseSpaceship`, for example, `DestroyerSpaceship`. I'm not sure if I should be using static methods or not. The examples I've seen online use static methods to instantiate `SKSpriteNode`s. This is what I came up with, but it's wrong. ``` // DestroyerSpaceship.h @interface DestroyerSpaceship : BaseSpaceship @property CGFloat missileThrust; - (id)makeDestroyerSpaceship; @end // DestroyerSpaceship.m @implementation DestroyerSpaceship - (id)makeDestroyerSpaceship{ DestroyerSpaceship *ship = [DestroyerSpaceship baseSpaceshipWithImageNamed:@"DestroyerShip"]; ship.engineThrust = 2.0; // ship doesn't have missileThrust, program crashes ship.missileThrust = 3.0; return ship; } @end ``` Ultimately, I want to be able to do something like this. ``` DestroyerSpaceship* a = [DestroyerSpaceship makeDestroyerSpaceship]; EvilSpaceship* b = [EvilSpaceship makeEvilSpaceship]; NiceSpaceship* c = [NiceSpaceship makeNiceSpaceship]; ``` And have them all inherit basic properties and methods from `BaseSpaceship`.

Original source