如何在SpriteKit中模糊场景?

Zan*_*ton 21 objective-c sprite-kit

如何在SpriteKit的SKScene中为所有节点添加高斯模糊(没有固定数量的节点)?稍后将在场景顶部添加标签,这将是我的暂停菜单.几乎任何事都有帮助!

这样的事情就是我想要的: 高斯暂停菜单

jem*_*ons 31

你要找的是一个SKEffectNode.它将CoreImage过滤器应用于自身(以及所有子节点).只需将它作为场景的根视图,给它一个CoreImage的模糊滤镜,就可以了.

例如,我设置了一个SKScene带有SKEffectNode它的第一个子节点和一个属性,root它拥有一个弱引用:

-(void)createLayers{
  SKEffectNode *node = [SKEffectNode node];
  [node setShouldEnableEffects:NO];
  CIFilter *blur = [CIFilter filterWithName:@"CIGaussianBlur" keysAndValues:@"inputRadius", @1.0f, nil];
  [node setFilter:blur];
  [self setRoot:node];
}
Run Code Online (Sandbox Code Playgroud)

这是我用来(动画!)场景模糊的方法:

-(void)blurWithCompletion:(void (^)())handler{
  CGFloat duration = 0.5f;
  [[self root] setShouldRasterize:YES];
  [[self root] setShouldEnableEffects:YES];
  [[self root] runAction:[SKAction customActionWithDuration:duration actionBlock:^(SKNode *node, CGFloat elapsedTime){
    NSNumber *radius = [NSNumber numberWithFloat:(elapsedTime/duration) * 10.0];
    [[(SKEffectNode *)node filter] setValue:radius forKey:@"inputRadius"];
  }] completion:handler];
}
Run Code Online (Sandbox Code Playgroud)

请注意,像你一样,我将它用作暂停屏幕,所以我光栅化了场景.如果你希望你的场景在模糊的同时进行动画制作,你应该这样setShouldResterize:NO.

如果你对动画转换到模糊不感兴趣,你可以随时将过滤器设置为初始半径10.0f左右,并setShouldEnableEffects:YES在想要打开它时做一个简单的操作.

另请参见:SKEffectNode类引用

更新:
见下面Markus的评论.他指出SKScene,事实上,它是一个子类SKEffectNode,因此你真的应该能够在场景中调用所有这些,而不是在节点树中任意插入效果节点.

  • 但是SKScene还不是SKEffectNode吗?你不能直接将过滤器添加到SKScene吗? (3认同)
  • 如果您将效果应用于整个场景,我敢打赌“PAUSED”标签和任何恢复游戏的按钮也会变得模糊。这就是为什么我有一个 `SKEffectNode` 作为我所有游戏内容的“画布”,而那个是场景的子元素。 (2认同)
  • 此外,这应该是公认的答案.它使用SpriteKit中已有的功能(不需要第三方代码,UIKit,CoreImage等). (2认同)
  • 请注意,只有效果节点的子节点不需要重绘时,`shouldRasterize = YES`才能阻止重绘.对于暂停屏幕,您可能希望在应用模糊之前"暂停"这些节点(或其常见的父节点),这样您就不会尝试以60fps的速度进行全屏模糊并融化GPU. (2认同)

Chu*_*ney 12

使用@Bendegúz的答案和代码从http://www.bytearray.org/?p=5360添加到此

我能够在我目前正在IOS 8 Swift中完成的游戏项目中使用它.返回SKSpriteNode而不是UIImage,做得有点不同.另请注意我的unwrapped currentScene.view!call是一个弱的GameScene引用,但应该根据你调用这些方法的位置使用self.view.frame.我的暂停屏幕在一个单独的HUD类中调用,因此为什么会这样.

我想这可以做得更优雅,也许更像是@ jemmons的回答.只是想在可能用SwriteKit项目编写的全部或部分Swift代码中帮助其他人.

func getBluredScreenshot() -> SKSpriteNode{

    create the graphics context
    UIGraphicsBeginImageContextWithOptions(CGSize(width: currentScene.view!.frame.size.width, height: currentScene.view!.frame.size.height), true, 1)

    currentScene.view!.drawViewHierarchyInRect(currentScene.view!.frame, afterScreenUpdates: true)

    // retrieve graphics context
    let context = UIGraphicsGetCurrentContext()

    // query image from it
    let image = UIGraphicsGetImageFromCurrentImageContext()

    // create Core Image context
    let ciContext = CIContext(options: nil)
    // create a CIImage, think of a CIImage as image data for processing, nothing is displayed or can be displayed at this point
    let coreImage = CIImage(image: image)
    // pick the filter we want
    let filter = CIFilter(name: "CIGaussianBlur")
    // pass our image as input
    filter.setValue(coreImage, forKey: kCIInputImageKey)

    //edit the amount of blur
    filter.setValue(3, forKey: kCIInputRadiusKey)

    //retrieve the processed image
    let filteredImageData = filter.valueForKey(kCIOutputImageKey) as CIImage
    // return a Quartz image from the Core Image context
    let filteredImageRef = ciContext.createCGImage(filteredImageData, fromRect: filteredImageData.extent())
    // final UIImage
    let filteredImage = UIImage(CGImage: filteredImageRef)

    // create a texture, pass the UIImage
    let texture = SKTexture(image: filteredImage!)
    // wrap it inside a sprite node
    let sprite = SKSpriteNode(texture:texture)

    // make image the position in the center
    sprite.position = CGPointMake(CGRectGetMidX(currentScene.frame), CGRectGetMidY(currentScene.frame))

    var scale:CGFloat = UIScreen.mainScreen().scale

    sprite.size.width  *= scale

    sprite.size.height *= scale

    return sprite


}


func loadPauseBGScreen(){

    let duration = 1.0

    let pauseBG:SKSpriteNode = self.getBluredScreenshot()

    //pauseBG.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
    pauseBG.alpha = 0
    pauseBG.zPosition = self.zPosition + 1
    pauseBG.runAction(SKAction.fadeAlphaTo(1, duration: duration))

    self.addChild(pauseBG)

}
Run Code Online (Sandbox Code Playgroud)

  • Chuck,标签上写着objective-c. (2认同)

Ben*_*gúz 10

这是我暂停屏幕的解决方案.它会截取屏幕截图,模糊它,然后用动画显示它.我想你应该这样做,如果你不想浪费很多fps.

-(void)pause {
    SKSpriteNode *pauseBG = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImage:[self getBluredScreenshot]]];
    pauseBG.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
    pauseBG.alpha = 0;
    pauseBG.zPosition = 2;
    [pauseBG runAction:[SKAction fadeAlphaTo:1 duration:duration / 2]];
    [self addChild:pauseBG];
}
Run Code Online (Sandbox Code Playgroud)

这是辅助方法:

- (UIImage *)getBluredScreenshot {
    UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 1);
    [self.view drawViewHierarchyInRect:self.view.frame afterScreenUpdates:YES];
    UIImage *ss = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    CIFilter *gaussianBlurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
    [gaussianBlurFilter setDefaults];
    [gaussianBlurFilter setValue:[CIImage imageWithCGImage:[ss CGImage]] forKey:kCIInputImageKey];
    [gaussianBlurFilter setValue:@10 forKey:kCIInputRadiusKey];

    CIImage *outputImage = [gaussianBlurFilter outputImage];
    CIContext *context   = [CIContext contextWithOptions:nil];
    CGRect rect          = [outputImage extent];
    rect.origin.x        += (rect.size.width  - ss.size.width ) / 2;
    rect.origin.y        += (rect.size.height - ss.size.height) / 2;
    rect.size            = ss.size;
    CGImageRef cgimg     = [context createCGImage:outputImage fromRect:rect];
    UIImage *image       = [UIImage imageWithCGImage:cgimg];
    CGImageRelease(cgimg);
    return image;
}
Run Code Online (Sandbox Code Playgroud)

  • 这个项目有点太迟了但我会保留以备将来参考:)非常感谢你! (2认同)