ios iphone sprite-kit skeffectnode

ios - ¿Cómo se puede crear un brillo alrededor de un sprite a través de SKEffectNode?



iphone sprite-kit (3)

Tengo un SKSpriteNode que me gustaría tener un brillo azul alrededor de los bordes para resaltar. Supongo que necesitaría convertir mi sprite en un hijo de un SKEffectNode y luego crear / aplicar un filtro de algún tipo.

ACTUALIZACIÓN: Lo he investigado bastante con el enfoque de la respuesta elegida, y descubrí que SKEffectNode tiene un impacto considerable en el rendimiento, incluso si lo tiene configurado en shouldRasterize y no se ha definido "sin filtro". Mi conclusión es que si su juego requiere más de 10 objetos en movimiento a la vez, no pueden involucrar un SKEffectNode aunque se SKEffectNode .

Es probable que mi solución implique imágenes / animaciones luminosas pre-renderizadas, ya que SKEffectNode no lo va a cortar para mis requisitos.

Si alguien tiene una idea de lo que me estoy perdiendo, ¡apreciaría escuchar lo que sabes!

Estoy aceptando una respuesta porque logra lo que pedí, pero quería agregar estas notas a cualquiera que busque ir por esta ruta, para que pueda estar al tanto de algunos problemas con el uso de SKEffectNode .


La respuesta de @Rickster es genial. Como tengo una baja reputación, aparentemente no puedo agregar este código como comentario al suyo. Espero que esto no rompa las reglas de propiedad de . No estoy tratando de usar su representante de ninguna manera.

Aquí está el código que hace lo que él describe en su respuesta:

Encabezamiento:

// 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

Implementación:

#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

En uso:

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


Podría usar un SKShapeNode detrás del sprite y definir un resplandor usando strokeColor propiedades glowWidth y strokeColor . Si lo dimensionas y posicionas correctamente, esto debería darte la apariencia de un resplandor. Esto no le brinda muchas opciones de personalización, pero me imagino que es mucho más fácil que usar un CIFilter con un SKEffectNode que es probablemente la otra opción lógica que tiene para esto.


Puede crear un efecto de brillo en Core Image creando una subclase CIFilter que compone varios filtros incorporados. Tal filtro involucraría pasos como estos:

  1. Crea una imagen para usarla como el brillo azul. Probablemente hay algunas maneras decentes de hacer esto; una es usar CIColorMatrix para crear una versión monocromática de la imagen de entrada.
  2. Escala y difumina la imagen luminosa ( CIAffineTransform + CIGaussianBlur ).
  3. Componga la imagen de entrada original sobre la imagen luminosa ( CISourceOverCompositing ).

Una vez que tenga una subclase CIFilter que hace todo eso, puede usarla con un SKEffectNode para obtener un brillo en tiempo real alrededor de los hijos del nodo de efecto. Aquí se ejecuta en la plantilla Xcode "Sprite Kit de juego" en un iPad 4:

Lo puse en marcha en unos minutos haciendo una selección de la clase de filtro personalizado utilizada para un efecto similar en la presentación de Scene Kit de WWDC 2013: obténgala del paquete WWDC Sample Code en developer.apple.com/downloads y busque para la clase ASCGlowFilter . (Si desea usar ese código en iOS, deberá cambiar la parte NSAffineTransform para usar CGAffineTransform . También reemplazé las propiedades centerX y centerY con un parámetro inputCenter de tipo CIVector para que Sprite Kit pueda centrar automáticamente el efecto en el duende.)

¿Dije brillo "en tiempo real"? ¡Sip! Eso es corto para "realmente come tiempo de CPU". Observe en la captura de pantalla que ya no está vinculado a 60 fps, incluso con una sola nave espacial, y con el software OpenGL ES en el simulador de iOS, se ejecuta a la velocidad de la presentación de diapositivas. Si estás en la Mac, probablemente tengas silicona de sobra ... pero si quieres hacer esto en tu juego, ten en cuenta algunas cosas:

  • Probablemente hay algunas formas de obtener un mejor rendimiento del propio filtro. Juega con diferentes filtros de CI y podrías ver algunas mejoras (hay varios filtros de desenfoque en Core Image, algunos de los cuales serán más rápidos que los de Gauss). Además, los efectos de desenfoque de la nota tienden a estar vinculados al fragmento-sombreador, por lo que cuanto más pequeña sea la imagen y más pequeño sea el radio del brillo, mejor.
  • Si desea tener múltiples brillos en una escena, considere convertir a todos los sprites brillantes en hijos del mismo nodo de efecto, que los convertirá a todos en una sola imagen, luego aplique el filtro una vez.
  • Si los sprites que se iluminan no cambian mucho (por ejemplo, si nuestra nave espacial no estaba girando), el ajuste shouldRasterize a YES en el nodo de efecto debería ayudar mucho. (En realidad, en este caso, podría obtener alguna mejora al rotar el nodo de efecto en lugar del sprite que contiene).
  • ¿Realmente necesitas brillo en tiempo real ? Al igual que con muchos efectos gráficos en los juegos, obtendrás un rendimiento mucho mejor si lo simulas. Haz una nave espacial borrosa y azul en tu editor de gráficos favorito y colócalo en la escena como otro sprite.