Как размыть сцену в SpriteKit?

Как добавить gaussian blur ко всем узлам (нет фиксированного количества узлов) в SKScene в SpriteKit? Метка будет добавлена ​​поверх сцены позже, это будет мое меню паузы. Почти все поможет!

Что-то вроде этого – то, что я собираюсь сделать: Меню гауссовой паузы

Вы ищете 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]; } 

И вот метод, который я использую для (анимации!) Размытия моей сцены:

 -(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]; } 

Заметьте, что, как и вы, я использую это как экран паузы, поэтому я растеризую сцену. Если вы хотите, чтобы ваша сцена анимации была размыта, вам, вероятно, следует setShouldResterize: NO .

И если вам не интересно анимацию перехода к размытию, вы всегда можете просто установить фильтр на начальный радиус 10.0f или около того и сделать простой setShouldEnableEffects:YES когда вы хотите включить его.

См. Также: Ссылка на class SKEffectNode

ОБНОВИТЬ:
См. Комментарий Маркуса ниже. Он указывает, что SKScene , по сути, является подclassом SKEffectNode , поэтому вы действительно должны иметь возможность вызывать все это на самой сцене, а не произвольно вставлять узел эффекта в свое дерево узлов.

Чтобы добавить к этому, используя ответ и код @ Bendegúz от http://www.bytearray.org/?p=5360

Я смог заставить это работать в моем текущем игровом проекте, который выполняется в IOS 8 Swift. Сделано несколько иначе, возвращая SKSpriteNode вместо UIImage. Также обратите внимание, что мой развернутый currentScene.view! вызов относится к слабой ссылке GameScene, но должен работать с self.view.frame на основе того, где вы вызываете эти методы. Мой экран паузы вызывается в отдельном classе HUD, поэтому это так.

Я бы предположил, что это можно сделать более элегантно, возможно, больше похоже на ответ @ jemmons. Просто хотел помочь кому-то еще, кто пытается это сделать в проектах SpriteKit, написанных во всем или в каком-то 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) } 

Это мое решение для экрана паузы. Это займет скриншот, размывает его и после этого покажет его с анимацией. Я думаю, вы должны это сделать, если не хотите тратить много кадров в секунду.

 -(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]; } 

И это вспомогательный метод:

 - (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; } 

Это еще один пример того, как это сделать в Swift 2 без слоев:

 func blurWithCompletion() { let duration: CGFloat = 0.5 let filter: CIFilter = CIFilter(name: "CIGaussianBlur", withInputParameters: ["inputRadius" : NSNumber(double:1.0)])! scene!.filter = filter scene!.shouldRasterize = true scene!.shouldEnableEffects = true scene!.runAction(SKAction.customActionWithDuration(0.5, actionBlock: { (node: SKNode, elapsedTime: CGFloat) in let radius = (elapsedTime/duration)*10.0 (node as? SKEffectNode)!.filter!.setValue(radius, forKey: "inputRadius") })) 

}

Обновление Swift 3: это ответ @Chuck Gaffney, обновленный для Swift 3. Я знаю, что этот вопрос отмечен как цель-c, но эта страница занимает второе место в Google для «быстрого размытия spritekit». Я изменил currentScene на себя .

  func getBluredScreenshot() -> SKSpriteNode{ //create the graphics context UIGraphicsBeginImageContextWithOptions(CGSize(width: self.view!.frame.size.width, height: self.view!.frame.size.height), true, 1) self.view!.drawHierarchy(in: self.view!.frame, afterScreenUpdates: true) // retrieve graphics 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?.value(forKey: kCIOutputImageKey) as! CIImage // return a Quartz image from the Core Image context let filteredImageRef = ciContext.createCGImage(filteredImageData, from: 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 = CGPoint(x: self.frame.midX, y: self.frame.midY) let scale:CGFloat = UIScreen.main.scale sprite.size.width *= scale sprite.size.height *= scale return sprite } func loadPauseBGScreen(){ let duration = 1.0 let pauseBG:SKSpriteNode = self.getBluredScreenshot() pauseBG.alpha = 0 pauseBG.zPosition = self.zPosition + 1 pauseBG.run(SKAction.fadeAlpha(to: 1, duration: duration)) self.addChild(pauseBG) } 

Swift 4:

добавьте это в свою игруScene, если вы хотите размыть все на сцене:

 let blur = CIFilter(name:"CIGaussianBlur",withInputParameters: ["inputRadius": 10.0]) self.filter = blur self.shouldRasterize = true self.shouldEnableEffects = false 

измените self.shouldEnableEffects = true, если вы хотите его использовать.

Давайте будем гением компьютера.