如何让textureFromNode为视网膜显示生成纹理?

时间:2014-08-07 15:27:51

标签: ios cocoa sprite-kit

平台:XCode 6.0 + iOS 7.0.4

在iPad mini视网膜上运行以下代码,并期望tmpLblnode的图像呈现相同,因为后者通过textureFromNode使用前者的纹理进行初始化。实际上tmpLbl是一个SKLabelNode,呈现视网膜分辨率但node(应该是副本)模糊不清,看起来是一半的双倍缩放 - 纹理。

我看到一篇帖子建议将源(tmpLbl放在下面的示例中),但它没有区别,如样本所示。

任何建议: 1.如何使用任何方法来完成这项工作 2.如何在没有在屏幕上呈现源节点(tmpLbl)的情况下完成这项工作

示例代码:

- (void)didMoveToView:(SKView *)view {
    SKLabelNode *tmpLbl = [SKLabelNode labelNodeWithFontNamed: @"Futura"];
    tmpLbl.position = CGPointMake(100, 100);
    [self addChild: tmpLbl];

    tmpLbl.fontSize = 125;
    tmpLbl.fontColor = [SKColor colorWithRed:0.8 green:0.0 blue:0.0 alpha:1.0];
    tmpLbl.text = @"a";

    SKTexture *texture = [view textureFromNode: tmpLbl];
    SKNode *node = [SKSpriteNode spriteNodeWithTexture:texture];
    node.position = CGPointMake(200, 130);
    [self addChild:node];
    return;
}

渲染结果;左边" a"是通过SKLabelNode,右侧是通过textureFromNode和spriteNodeWithTexture:

enter image description here

4 个答案:

答案 0 :(得分:2)

问题的解决方案:传递给textureFromNode的源节点树必须但不足以成为其他人建议的场景的一部分。返回纹理的另一个条件是"视网膜分辨率"是textureFromNode:的调用不是早于(根据我的实验结果)而不是第一次调用SKScene update:。 (更新:在XCode 5.0.1 textureFromNode:上必须在第二次调用update:期间或之后调用; textureFromNode在第一次调用期间仍然会产生非视网膜纹理。)

只需将上述问题中的代码移动到update:函数,生成的纹理就会拥有视网膜渲染的所有像素。令人沮丧的是纹理将在两个方向上都是源节点(树)的两倍大小,并且使用返回的精灵初始化一个新的精灵将产生一个双倍大小的精灵(用插值渲染)。很明显,SKTexture类具有内部规模'财产,就像UIImage一样。 SpriteKit在使用' @ 2x'从文件创建纹理时设置此属性。后缀,但不是通过textureFromNode创建纹理时。这非常不幸,因为这会强制应用程序开发人员手动跟踪比例,除非从图像资源创建纹理。

以下更新的示例包含与问题中原始内容相比的以下更改:

  • textureFromNode:(以及其他代码)移至update:;谁有一个更好的地方,仍然会产生视网膜大小的演绎?
  • 检测显示分辨率是否为视网膜,并根据需要将新精灵调整为源精灵的大小

通过这些改变,这两个' a'图像呈现相同。


// retina resolution will be not applied if textureFromNode is called before SKScene::update():
-(void)update:(CFTimeInterval)currentTime {
    static int cnt = 0;

    if (cnt == 1) // can be cnt == 0 on XCode 6.0
    {
        double scale=1.0;
        if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0)) {
            // the texture returned by textureFromNode is double-size on retina displays; must explicitly make the sprite half the size of the texture:
            scale=0.5;
        }

        SKLabelNode *tmpLbl = [SKLabelNode labelNodeWithFontNamed: @"Futura"];
        tmpLbl.position = CGPointMake(100, 100);
        // must be part of the scene or else retina resolution will be not applied:
        [self addChild: tmpLbl];

        tmpLbl.fontSize = 125;
        tmpLbl.fontColor = [SKColor colorWithRed:0.8 green:0.0 blue:0.0 alpha:1.0];
        tmpLbl.text = @"a";

        SKTexture *texture = [self.view textureFromNode: tmpLbl];
        SKNode *node = [SKSpriteNode spriteNodeWithTexture:texture size: CGSizeMake(texture.size.width*scale, texture.size.height*scale)];
        node.position = CGPointMake(200, 130);
        [self addChild:node];

        // was added to the scene only to force retina resolution rendering:
        // [tmpLbl removeFromParent];
    }
    ++cnt;
}

使用更新的代码渲染结果。右侧图像不再模糊:

Rendering result:

答案 1 :(得分:1)

感谢您在更新后执行textureFromNode的想法:调用方法。

至于一个仍会产生视网膜大小再现的更好的地方,我发现在dispatch_once块中调用put光栅化代码更清楚,以避免不必要的计数器增量。

在我的GameScene(Swift)中:

var rasterizationToken: dispatch_once_t = 0

override func didFinishUpdate() {
    dispatch_once(&rasterizationToken) {
        self.rasterizeLayers() //calls textureFromNode inside
    }
}

在update()之后调用didFinishUpdate(),因此视网膜分辨率仍然存在。

答案 2 :(得分:0)

您的标签应添加到场景中,否则它将在渲染纹理中具有非视网膜分辨率。

也许是重复的问题

Sprite Kit - Low Resolution Texture on Retina when using textureFromNode

答案 3 :(得分:0)

我在Swift 3中为SKLabelNode开发了一个解决方法:

extension SKLabelNode {

    func pixelated() {
        let v = SKView()
        self.fontSize *= 4
        let texture = v.texture(from: self)
        let textureSize = CGSize(width: (texture?.size().width)!/4, height: (texture?.size().height)!/4)
        let node = SKSpriteNode(texture: texture, color: .clear, size: textureSize)
        node.position = self.position
        node.anchorPoint = CGPoint(x: 0.5, y: 0.0)
        self.parent?.addChild(node)
        self.removeFromParent()
    }

}