SKSpriteNode 的正确子类化和实例初始化

Posted

技术标签:

【中文标题】SKSpriteNode 的正确子类化和实例初始化【英文标题】:Proper subclassing of SKSpriteNode and instance initialization 【发布时间】:2015-08-31 15:08:33 【问题描述】:

我正在尝试继承 SKSpriteNode 以便我可以使用它来为游戏创建具有不同属性的不同角色。我知道这应该是一项微不足道的任务,但我很难理解如何在创建实例时初始化变量。 在标题中我有:

#import <SpriteKit/SpriteKit.h>

@interface MyClass : SKSpriteNode 
@property int myVariable;
@end

在实现中:

#import "MyClass.h"
@interface MyClass ()
@end

@implementation MyClass
@synthesize myVariable;

- (id)init 
    if (self = [super init]) 
        myVariable = 100;
    
    return self;

当我在场景中创建节点时:

@implementation GameScene

    MyClass *mySprite;


- (void)didMoveToView: (SKView *) view

    mySprite = [MyClass spriteNodeWithImageNamed:@"image.png"];

myVariable 的值为 0,我假设这意味着 spriteNodeWithImageNamed 没有执行 init 方法。如果我使用:

mySprite = [[MyClass alloc] init]; // or mySprite = [MyClass new];
mySprite = [MyClass spriteNodeWithImageNamed:@"image.png"];

变量名被正确设置为 100,但随后被 spriteNodeWithImageNamed 恢复为 0。

我也试过了:

mySprite = [[MyClass alloc] init]; // or mySprite = [MyClass new];
[mySprite setTexture:[SKTexture textureWithImageNamed:@"image.png"]];

在这种情况下,myVariable 的值是正确的,但是当我将节点添加到场景时没有图像出现。使用 spriteNodeWithImageNamed 时调用什么 init 方法?我是否不正确地覆盖了 SKSpriteNode 初始化方法?

【问题讨论】:

使用现代 obj-c 和 Xcode,无需 @synthesize 【参考方案1】:

您已经正确地创建了一个类,但是您需要记住您正在调用工厂方法spriteNodeWithImageNamed,然后它会自行初始化和分配。因此,如果您想使用自定义的 init 方法,您还需要覆盖工厂方法。

在您的 MyClass 实现中,覆盖调用您的初始化程序的方法:

+ (instancetype) spriteNodeWithImageNamed:(NSString *)imageName
    MyClass *newClass = [[MyClass alloc] init];
    SKTexture *spriteTexture = [SKTexture textureWithImageNamed:imageName];
    newClass.size = spriteTexture.size;
    newClass.texture = spriteTexture;
    return newClass;

希望这会有所帮助,如果您有任何问题,请告诉我。

*EDIT:我实际上有点想放弃这个,只是因为 SKSpriteNode 没有init 方法(或者至少我们不知道)。因此,除非您想深入了解 Apple 的奥秘,否则请这样做:

+ (instancetype) spriteNodeWithImageNamed:(NSString *)imageName
    MyClass *newClass = [[MyClass alloc] initWithImageNamed:imageName];
    newClass.myVariable = 100;
    return newClass;

您可以改写 initWithImage 以放入 myVariable,但重点是我会避免尝试将 init 初始化程序与 SKSpriteNode 一起使用。

【讨论】:

谢谢。这是有道理的,现在 myVariable 的值是正确的。但是,该节点并没有出现在场景中。它与加载纹理有关吗?根据手册:“Sprite Kit 尽可能推迟加载和准备纹理”。在以下情况下加载纹理数据: 调用纹理对象的 size 方法。另一种方法称为需要纹理的大小,例如创建一个使用纹理对象的新 SKSpriteNode 对象。调用其中一种预加载方法。由于我们不再从 SKSpriteNode 调用纹理,这可能是原因吗? 好吧,你打电话给[super init] 所以你确实有一个 spriteNode 除了你添加的额外变量。纹理应该初始化得很好。你在看什么?如果是红色“X”,则图像不存在或名称输入错误。如果您什么也没看到,可能是因为 zPosition 可能未设置,它位于错误的位置/没有作为子对象添加,或者只是整个对象被释放并且它不存在。尝试仔细查看您的代码或在编辑中发布您如何处理节点,直到您将其添加为子节点以及您希望看到它的位置。 再次感谢。节点在那里,但它的大小为 0。这就是精灵不可见的原因。虽然我可以使用一个动作使节点出现:[newClass runAction:[SKAction setTexture:texture resize:YES]];关于节点的其他事情仍然无法正常工作:我添加到它的子节点是不可见的,并且没有检测到联系人。我想当 SKSpriteNode 被子类化时,有些工厂初始化不会被复制。查看工厂实现以了解有关其方法的更多详细信息会很有趣。 是的,我想你会遇到一些问题,只是使用正常的初始化......检查我上面的编辑^。我只是使用 spriteNode 的 initWithImageNamed,它应该执行您遇到问题的后台初始化。 好的,现在可以了!我猜 initWithImageNamed 方法不需要 init。非常感谢您的帮助。非常感谢。【参考方案2】:

这是我的做法。创建自己的自定义 init 方法,只需调用默认 spritenode initWithImage。

-(id)initShipWithType:(ShipType)type 
    self = [super initWithImageNamed:[self getShipSprite:type]];
    if (self) 

    
    return self;

【讨论】:

以上是关于SKSpriteNode 的正确子类化和实例初始化的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 Nib 的情况下实例化和调用 UIView 子类

UITableViewHeaderFooterView 的正确子类化和自动布局

正确子类化和重用 UITableViewHeaderFooterView

使用 NSLayoutConstraint 实例化和设置 UIButton 子类的大小

SKSpriteNode 子类:移除所有关节的方法

如何在Swift中正确地从父节点中删除SKSpriteNode的子类(要从数组中移除,屏幕上的空格......)?