web-dev-qa-db-ja.com

SpriteKitでシーンをぼかすにはどうすればよいですか?

SpriteKitのSKSceneのすべてのノード(ノードの数は固定されていません)にガウスぼかしを追加するにはどうすればよいですか?後でシーンの上にラベルが追加されます。これが私の一時停止メニューになります。ほとんど何でも役に立ちます!

このようなものが私が目指しているものです: Gaussian pause menu

21
Zane Helton

あなたが探しているのはSKEffectNodeです。 CoreImageフィルターをそれ自体(したがってすべてのサブノード)に適用します。シーンのルートビューにし、CoreImageのブラーフィルターの1つを指定するだけで、設定が完了します。

たとえば、最初の子ノードとして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を実行することができます。 。

参照: SKEffectNodeクラス参照

更新:
以下のMarkusのコメントを参照してください。彼は、SKSceneは実際にはSKEffectNodeのサブクラスであるため、エフェクトノードを任意に挿入するのではなく、シーン自体でこれらすべてを呼び出すことができるはずだと指摘しています。ノードツリー。

31
jemmons

@Bendegúzの回答とコードを使用してこれに追加するには http://www.bytearray.org/?p=536

IOS 8 Swiftで行われている現在のゲームプロジェクトでこれを機能させることができました。UIImageの代わりにSKSpriteNodeを返すことで、少し異なる方法で実行しました。また、ラップされていないcurrentSceneにも注意してください。 view!呼び出しは、弱いGameScene参照に対するものですが、これらのメソッドを呼び出す場所に基づいてself.view.frameで機能するはずです。一時停止画面は別のHUDクラスで呼び出されるため、これが当てはまります。

これはもっとエレガントに、おそらく@jemmonsの答えのようにできると思います。すべてまたは一部のSwiftコードで記述されたSpriteKitプロジェクトで、これを実行しようとしている他の人を支援したかっただけです。

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)

}
12
Chuck Gaffney

これが一時停止画面の解決策です。スクリーンショットを撮り、ぼかしてから、アニメーションで表示します。多くの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];
}

そしてこれはヘルパーメソッドです:

- (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;
}
10
Bendegúz

これは、レイヤーなしで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")

}))

}

2
Victor --------

Swift 4:

シーン内のすべてをぼかしたい場合は、これをgameSceneに追加します。

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

使用する場合は、self.shouldEnableEffects = trueを変更します。

2
Alex Bailey

Swift 3 Update:これは@ChuckGaffneyの回答ですSwift 3.この質問はobjective-cとタグ付けされていますが、このページはGoogleで「 Swift spritekitblur」。currentSceneselfに変更しました。

    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)

}
2
riot