All SKNode classes provide a userData
property which can be used for exactly this purpose.
You can write a Objective-C category through which you can add additional properties without needing to subclass. A proven pattern below.
Interface:
#import "YourCustomObject.h"
@interface SKNode (customObject)
@property (nonatomic) YourCustomObject* yourCustomObject;
@end
Implementation:
static NSString* YourCustomObjectUserDataKey = @"YourCustomObjectUserDataKey";
@implementation SKNode (customObject)
-(NSMutableDictionary*) internal_getOrCreateUserData
{
NSMutableDictionary* userData = self.userData;
if (userData == nil)
{
userData = [NSMutableDictionary dictionary];
self.userData = userData;
}
return userData;
}
-(void) setYourCustomObject:(YourCustomObject*)customObject
{
NSMutableDictionary* userData = [self internal_getOrCreateUserData];
[userData setObject: customObject forKey:YourCustomObjectUserDataKey];
}
-(YourCustomObject*) yourCustomObject
{
NSMutableDictionary* userData = [self internal_getOrCreateUserData];
return (YourCustomObject*)[userData objectForKey:YourCustomObjectUserDataKey];
}
@end
Replace YourCustomObject
with your actual class name.
The category can be extended to host multiple properties of course, you don't have to write a separate category for each property. The category does not have to be on SKNode either, if you need an additional property but only on SKSpriteNode you would make this a category on SKSpriteNode.
Very often you'll need access to the owning node in YourCustomObject
.
For that reason it's a good idea to provide an initializer and/or property that takes the owning node as input. I prefer readonly properties and a custom initializer because usually you don't want the owner to change throughout the object's lifetime. Very important: this property must be weak
to not create a retain cycle.
@interface YourCustomObject
@property (weak, readonly) SKNode* owningNode;
+(instancetype) customObjectWithOwningNode:(SKNode*)owningNode;
@end
@implementation YourCustomObject
+(instancetype) customObjectWithOwningNode:(SKNode*)owningNode
{
return [[self alloc] initWithOwningNode:owningNode];
}
-(id) initWithOwningNode:(SKNode*)owningNode
{
self = [super init];
if (self)
{
_owningNode = owningNode;
}
return self;
}
@end
You can then create and assign your custom object within a node class:
YourCustomObject* customObject = [YourCustomObject customObjectWithOwningNode:self];
self.yourCustomObject = customObject;