【问题标题】:How can you create a glow around a sprite via SKEffectNode如何通过 SKEffectNode 在精灵周围创建发光
【发布时间】:2013-10-13 09:20:12
【问题描述】:

我有一个SKSpriteNode,我想在它的边缘周围有一个蓝色的光晕,以便突出显示。我猜我需要让我的精灵成为SKEffectNode 的孩子,然后创建/应用某种过滤器。

更新:我已经用所选答案的方法对此进行了深入调查,发现SKEffectNode 对性能有相当大的影响,即使您将其设置为shouldRasterize 并定义了“无过滤器”。我的结论是,如果您的游戏一次需要超过 10 个移动对象,那么即使光栅化,它们也不能涉及 SKEffectNode

我的解决方案可能会涉及预渲染的发光图像/动画,因为 SKEffectNode 不会根据我的要求进行裁剪。

如果有人对我遗漏的任何事情有洞察力,我会很高兴听到你所知道的一切!

我接受答案是因为它确实达到了我的要求,但想将这些注释添加给任何想要走这条路的人,这样您就可以了解使用 SKEffectNode 时遇到的一些问题。

【问题讨论】:

  • 我尝试了一些方法,但还没有提出解决方案 - 无法让 EffectNode “描边”图像。

标签: ios iphone sprite-kit skeffectnode


【解决方案1】:

您可以在精灵后面使用SKShapeNode,并使用它的glowWidthstrokeColor 属性定义发光。如果您调整大小并正确放置它,这应该会给您带来发光的外观。这并没有为您提供很多自定义选项,但我想这比使用 CIFilterSKEffectNode 容易得多,这可能是您为此提供的另一个合乎逻辑的选项。

【讨论】:

  • 如何使用 SKShapeNode 获得效果?您的意思是创建一个与 SKspriteNode 的轮廓相匹配的形状,然后使其发光并将其放在 SKSpriteNode 后面?精灵不是方形的,有没有办法创建一个匹配精灵的 SKShapeNode ?物理引擎会为 collison 做到这一点吗?
  • 正如 rickster 所说,这基本上是在伪造它,但在此过程中节省了大量性能。您将创建精灵的路径并在SKShapeNode 中的精灵后面使用。我会用一个固定的关节或类似的东西来连接它,然后关闭它的物理特性,让它基本上跟随精灵 - 让精灵处理物理/碰撞。
  • 我在问是否有办法创建 SKSpriteNode 的 SKShapeNode 路径。最好有一种方法来创建一个遵循 SKSpriteNode 轮廓的 SKShapeNode 路径。
  • 不,没有自动的方法。有一些外部工具可以渲染到 CGPath,但遗憾的是,SpriteKit 没有内置任何工具。
  • @juggleware SKShapeNode 包裹在 SKEffectNode 中,shouldRasterize 属性设置为 YES,对性能非常友好。
【解决方案2】:

你可以通过创建一个CIFilter 子类来在Core Image 中创建一个发光效果,该子类包含多个内置过滤器。这样的过滤器将涉及以下步骤:

  1. 创建一个用作蓝色光晕的图像。可能有一些不错的方法可以做到这一点;一种是使用CIColorMatrix 创建输入图像的单色版本。
  2. 放大和模糊发光图像 (CIAffineTransform + CIGaussianBlur)。
  3. 在发光​​图像上合成原始输入图像 (CISourceOverCompositing)。

一旦你有一个 CIFilter 子类可以完成所有这些,你可以将它与 SKEffectNode 一起使用,以在效果节点的子类周围实时发光。它在 iPad 4 上的“Sprite Kit Game”Xcode 模板中运行:

我通过在 WWDC 2013 的 Scene Kit 演示文稿中使用用于类似效果的自定义过滤器类在几分钟内启动并运行它——从developer.apple.com/downloads 的 WWDC 示例代码包中获取它,然后寻找ASCGlowFilter 类。 (如果您想在 iOS 上使用该代码,则需要将 NSAffineTransform 部分更改为使用 CGAffineTransform。我还将 centerXcenterY 属性替换为 inputCenter 类型的参数 @ 987654336@ 所以 Sprite Kit 可以自动将效果居中在精灵上。)

我说的是“实时”发光吗?对!这是“真正消耗 CPU 时间”的缩写。请注意,在屏幕截图中,即使只有一艘宇宙飞船,它也不再固定在 60 fps - 并且使用 iOS 模拟器上的软件 OpenGL ES 渲染器,它以幻灯片速度运行。如果你在 Mac 上,你可能有多余的硅片......但如果你想在你的游戏中这样做,请记住一些事情:

  • 可能有一些方法可以让过滤器本身获得更好的性能。使用不同的 CI 过滤器,您可能会看到一些改进(Core Image 中有几个模糊过滤器,其中一些肯定会比高斯更快)。另请注意,模糊效果往往受片段着色器限制,因此图像越小,发光半径越小越好。
  • 如果您想在一个场景中拥有多个发光体,请考虑将所有发光精灵设置为同一个效果节点的子节点 - 这会将它们全部渲染到一张图像中,然后应用一次过滤器。
  • 如果要发光的精灵没有太大变化(例如,如果我们的宇宙飞船没有旋转),在效果节点上将shouldRasterize 设置为YES 应该会有很大帮助。 (实际上,在这种情况下,您可以通过旋转效果节点而不是其中的精灵来获得一些改进。)
  • 您真的需要实时发光吗?与游戏中许多漂亮的图形效果一样,如果您伪造它,您将获得更好的性能。在您最喜欢的图形编辑器中制作一个模糊的蓝色宇宙飞船,并将其作为另一个精灵放入场景中。

【讨论】:

  • 是的,这非常接近我想要的。我打算光栅化,而不是实时渲染每一帧。我会试试这个。
  • @rickster,你能发布你如何完成这 3 个步骤的代码吗?顺便说一句,答案很好。
  • 我使用了来自 Apple 示例代码的过滤器,稍作改动——请参阅上面的下载链接。
  • 哇,在测试这个方向时,我发现使用 SKEffectNode,即使 shouldRasterize 设置为 YES 并且没有实时渲染,如果我有 10 个对象移动,帧率也会受到相当大的影响(20fps)同时(未定义过滤器)。我可以看到我只需要将这种事情作为预渲染的图像/动画来处理。
  • 我接受这个答案是因为它确实使用 SKEffectNode 实现了发光效果,并且对所涉及的警告非常彻底。
【解决方案3】:

@rickster 的回答很棒。由于我的代表很低,显然不允许我将此代码添加为对他的评论。我希望这不会违反 stackoverflow 的适当规则。我不想以任何方式使用他的代表。

这是他在回答中描述的代码:

标题:

//  ENHGlowFilter.h
#import <CoreImage/CoreImage.h>

@interface ENHGlowFilter : CIFilter

@property (strong, nonatomic) UIColor *glowColor;
@property (strong, nonatomic) CIImage *inputImage;
@property (strong, nonatomic) NSNumber *inputRadius;
@property (strong, nonatomic) CIVector *inputCenter;

@end

//Based on ASCGLowFilter from Apple

实施:

#import "ENHGlowFilter.h"

@implementation ENHGlowFilter

-(id)init
{
    self = [super init];
    if (self)
    {
        _glowColor = [UIColor whiteColor];
    }
    return self;
}

- (NSArray *)attributeKeys {
    return @[@"inputRadius", @"inputCenter"];
}

- (CIImage *)outputImage {
    CIImage *inputImage = [self valueForKey:@"inputImage"];
    if (!inputImage)
        return nil;

    // Monochrome
    CIFilter *monochromeFilter = [CIFilter filterWithName:@"CIColorMatrix"];
    CGFloat red = 0.0;
    CGFloat green = 0.0;
    CGFloat blue = 0.0;
    CGFloat alpha = 0.0;
    [self.glowColor getRed:&red green:&green blue:&blue alpha:&alpha];
    [monochromeFilter setDefaults];
    [monochromeFilter setValue:[CIVector vectorWithX:0 Y:0 Z:0 W:red] forKey:@"inputRVector"];
    [monochromeFilter setValue:[CIVector vectorWithX:0 Y:0 Z:0 W:green] forKey:@"inputGVector"];
    [monochromeFilter setValue:[CIVector vectorWithX:0 Y:0 Z:0 W:blue] forKey:@"inputBVector"];
    [monochromeFilter setValue:[CIVector vectorWithX:0 Y:0 Z:0 W:alpha] forKey:@"inputAVector"];
    [monochromeFilter setValue:inputImage forKey:@"inputImage"];
    CIImage *glowImage = [monochromeFilter valueForKey:@"outputImage"];

    // Scale
    float centerX = [self.inputCenter X];
    float centerY = [self.inputCenter Y];
    if (centerX > 0) {
        CGAffineTransform transform = CGAffineTransformIdentity;
        transform = CGAffineTransformTranslate(transform, centerX, centerY);
        transform = CGAffineTransformScale(transform, 1.2, 1.2);
        transform = CGAffineTransformTranslate(transform, -centerX, -centerY);

        CIFilter *affineTransformFilter = [CIFilter filterWithName:@"CIAffineTransform"];
        [affineTransformFilter setDefaults];
        [affineTransformFilter setValue:[NSValue valueWithCGAffineTransform:transform] forKey:@"inputTransform"];
        [affineTransformFilter setValue:glowImage forKey:@"inputImage"];
        glowImage = [affineTransformFilter valueForKey:@"outputImage"];
    }

    // Blur
    CIFilter *gaussianBlurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
    [gaussianBlurFilter setDefaults];
    [gaussianBlurFilter setValue:glowImage forKey:@"inputImage"];
    [gaussianBlurFilter setValue:self.inputRadius ?: @10.0 forKey:@"inputRadius"];
    glowImage = [gaussianBlurFilter valueForKey:@"outputImage"];

    // Blend
    CIFilter *blendFilter = [CIFilter filterWithName:@"CISourceOverCompositing"];
    [blendFilter setDefaults];
    [blendFilter setValue:glowImage forKey:@"inputBackgroundImage"];
    [blendFilter setValue:inputImage forKey:@"inputImage"];
    glowImage = [blendFilter valueForKey:@"outputImage"];

    return glowImage;
}


@end

使用中:

@implementation ENHMyScene //SKScene subclass

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */
        [self setAnchorPoint:(CGPoint){0.5, 0.5}];
        self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];

        SKEffectNode *effectNode = [[SKEffectNode alloc] init];
        ENHGlowFilter *glowFilter = [[ENHGlowFilter alloc] init];
        [glowFilter setGlowColor:[[UIColor redColor] colorWithAlphaComponent:0.5]];
        [effectNode setShouldRasterize:YES];
        [effectNode setFilter:glowFilter];
        [self addChild:effectNode];
        _effectNode = effectNode;
    }
    return self;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */

    for (UITouch *touch in touches) {
        CGPoint location = [touch locationInNode:self];
        SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"];
        sprite.position = location;
        [self.effectNode addChild:sprite];
    }
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-26
  • 1970-01-01
  • 1970-01-01
  • 2011-09-19
  • 2017-01-07
  • 1970-01-01
相关资源
最近更新 更多