13

只是在玩 SpriteKit,并试图弄清楚如何将 SKNode 的“抓取”捕获到 UIImage 中。

使用 UIView(或 UIView 子类),我使用layer了视图的属性来渲染到图形上下文中。

例如。

#import <QuartzCore/QuartzCore.h>
+ (UIImage *)imageOfView:(UIView *)view {
    UIGraphicsBeginImageContextWithOptions(view.frame.size, YES, 0.0f);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [view.layer renderInContext:context];
    UIImage *viewShot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return viewShot;
}

SKNode 不是 UIView 的子类,因此似乎不受层的支持。

关于如何将给定的 SKNode 渲染到 UIImage 的任何想法?

4

2 回答 2

17

这将捕获整个场景:

CGRect bounds = self.scene.view.bounds;
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, [UIScreen mainScreen].scale);
[self drawViewHierarchyInRect:bounds afterScreenUpdates:YES];
UIImage* screenshotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

如果您只想要一个特定的节点分支,您可以在截屏之前隐藏您不想捕获的所有节点。您还可以使用转换为UIKit坐标的累积帧来仅​​捕获节点及其相关子节点的区域。

或者,您可以SKTexture从节点层次结构的特定部分获取:

SKTexture* tex = [self.scene.view textureFromNode:yourNode];

在 iOS 9 之前,没有办法将SKTexturea 转换回UIImage. 然而,现在它是微不足道的:

UIImage *image = [UIImage imageWithCGImage:tex.CGImage];
于 2013-12-17T09:11:11.733 回答
3

对于 7 年后查看此线程的任何人,我尝试使用上述方法,但它们产生的结果相对较慢。我想在屏幕外渲染整个 SKScene,因为我将图像渲染为视频的帧。这意味着我不能使用 LearnCocos2D 建议的第一种方法,因为它需要在屏幕上绘制视图,而第二种方法需要很长时间才能SKTextureUIImage. 您可以使用SKRendereriOS 11.0 中引入的新类将整个场景渲染到 UIImage,它利用了Metal,因此渲染速度相对较快。我能够SKScene在大约 0.013 秒内渲染 1920x1080!

你可以使用这个扩展:

  • 确保你import MetalKit
  • ignoreScreenScale参数指定输出图像是否应该是像素精确的。通常,如果您要在屏幕上显示图像,您会希望这是错误的。如果为假,则输出图像的大小将按设备的比例缩放,以使场景上的每个“点”在图像中占据与屏幕上相同数量的像素。当这是真的时,以像素为单位的输出图像的大小等于SKScenein 点的大小。

干杯!

extension SKScene {
    func toImage(ignoreScreenScale: Bool = false) -> UIImage? {
        guard let device = MTLCreateSystemDefaultDevice(),
              let commandQueue = device.makeCommandQueue(),
              let commandBuffer = commandQueue.makeCommandBuffer() else { return nil }

        let scale = ignoreScreenScale ? 1 : UIScreen.main.scale
        let size = self.size.applying(CGAffineTransform(scaleX: scale, y: scale))
        let renderer = SKRenderer(device: device)
        let renderPassDescriptor = MTLRenderPassDescriptor()

        var r = CGFloat.zero, g = CGFloat.zero, b = CGFloat.zero, a = CGFloat.zero
        backgroundColor.getRed(&r, green: &g, blue: &b, alpha: &a)

        let textureDescriptor = MTLTextureDescriptor()
        textureDescriptor.usage = [.renderTarget, .shaderRead]
        textureDescriptor.width = Int(size.width)
        textureDescriptor.height = Int(size.height)
        let texture = device.makeTexture(descriptor: textureDescriptor)

        renderPassDescriptor.colorAttachments[0].loadAction = .clear
        renderPassDescriptor.colorAttachments[0].texture = texture
        renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(
            red: Double(r),
            green: Double(g),
            blue: Double(b),
            alpha:Double(a)
        )

        renderer.scene = self
        renderer.render(withViewport: CGRect(origin: .zero, size: size), commandBuffer: commandBuffer, renderPassDescriptor: renderPassDescriptor)
        commandBuffer.commit()

        let image = CIImage(mtlTexture: texture!, options: nil)!
        let transformed = image.transformed(by: CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -image.extent.size.height))
        return UIImage(ciImage: transformed)
    }
}
于 2020-12-29T23:52:53.017 回答