ios objective-c sprite-kit ios9 sktexture
Un proyecto de muestra

ios - Guarde SKTexture generado en el archivo



objective-c sprite-kit (2)

He hecho muchas pruebas / pirateo con SKTextures. Mi juego utiliza SKTextures. Está escrito en Swift. Específicamente, he tenido muchos problemas con textureFromNode y textureFromNode: crop: y creando SKPhysicsBodies a partir de texturas. Estos métodos funcionaron bien en iOS 8, pero Apple los rompió por completo cuando lanzaron iOS 9.0. En ios 9.0, las texturas regresaban como nulas. Esas texturas nulas rompieron SKPhysicsBodies de las texturas.

Recientemente trabajé en la serialización / deserialización de SKTextures.

Algunas ideas / claves clave que puede investigar son:

  1. Ejecute ios 9.2. El personal de Apple mencionó que se han solucionado muchos problemas. https://forums.developer.apple.com/thread/17463 He encontrado que ios 9.2 ayuda con SKTextures pero no resolvió todos los problemas, especialmente los problemas de serialización.

  2. Pruebe PrefersOpenGL (configúrelo en "SÍ" como una propiedad personalizada Booleana en su configuración). Aquí hay una publicación sobre PrefersOpenGL en los foros de Apple Dev por parte del personal de Apple. https://forums.developer.apple.com/thread/19683 He observado que ios 9.x parece usar Metal por defecto en lugar de OpenGL. He encontrado que PrefersOpenGL ayuda con los problemas de SKTexture, pero aún no hace que mis SKShaders funcionen (escritos en GLSL).

  3. Cuando intenté serializar / deserializar nodos con SKTextures en ios 9.2, obtuve cuadros blancos en lugar de texturas visibles. Inspirado en los documentos Apple SKTexture que dicen: "Los datos de textura se cargan cuando:

El método de tamaño en el objeto de textura se llama.

Se llama otro método que requiere el tamaño de la textura, como la creación de un nuevo objeto SKSpriteNode que utiliza el objeto de textura.

Se llama a uno de los métodos de precarga (ver Precarga de los datos de textura).

Los datos de textura están preparados para renderizar cuando:

Un sprite o partícula que usa la textura es parte de un árbol de nodos que se está renderizando ".

... He pirateado una solución alternativa que crea una textura secundaria a partir de la llamada CGImage ():

// ios 9.2 workaround for white boxes on serialization let img = texture!.CGImage() let uimg = UIImage(CGImage: img) let ntex = SKTexture(image: uimg) let sprite = SKSpriteNode(texture: ntex, size: texture!.size())

Así que ahora mis SKSpriteNodes creados de esta manera parecen serializar / deserializar bien. Por cierto, solo invocando size () o creando un SKSpriteNode con la textura original no parece ser suficiente para reificar la textura en la memoria.

  1. No preguntaste sobre textureFromNode: crop: pero estoy agregando observaciones de todos modos por si acaso te ayuda: he encontrado que este método en ios 8 funcionaba (aunque los parámetros de recorte eran muy complicados y parecían requerir normalización con UIScreen). mainScreen (). scale) En ios 9.0, este método no funcionó en absoluto (devuelto nil). En ios 9.2 este método ahora funciona (ahora devuelve una textura no nula), pero la creación posterior de nodos de la textura no necesita la normalización de tamaño. Y, además, para que funcione la serialización / deserialización, descubrí que en última instancia tiene que hacer el n. ° 3 anterior.

Espero que esto te ayude. Me imagino que he luchado más que la mayoría con SKTextures ya que mi aplicación es tan dependiente de ellos.

He archivado un error para el problema a continuación. ¿Alguien con una buena solución?

Intento guardar una SKTexture en el archivo y volver a cargarla, pero no lo logro. El siguiente fragmento de código se puede copiar a GameScene.m en el proyecto de inicio de Xcode.

Uso textureFromNode en generateTexture , y esa parece ser la causa de mi problema. Si utilizo una textura de un sprite, el código funciona, y dos naves espaciales son visibles.

Este código funcionó en iOS 8 pero dejó de funcionar en Xcode7 e iOS 9. Solo quiero verificar que este es un error antes de presentar un informe de error. Mi preocupación es que hago algo mal con NSKeyedArchiver .

Sucede tanto en el simulador como en el dispositivo.

#import "GameScene.h" @implementation GameScene // Generates a texture - (SKTexture *)generateTexture { SKScene *scene = [[SKScene alloc] initWithSize:CGSizeMake(100, 100)]; SKShapeNode *shapeNode = [SKShapeNode shapeNodeWithRectOfSize:CGSizeMake(50, 50)]; shapeNode.position = CGPointMake(50, 50); shapeNode.strokeColor = SKColor.redColor; shapeNode.lineWidth = 10; [scene addChild:shapeNode]; SKTexture *texture = [self.view textureFromNode:scene]; //SKTexture *texture = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"].texture; // This works! return texture; } // Just generate a path - (NSString *)fullDocumentsPath { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *yourFileName = [documentsDirectory stringByAppendingPathComponent:@"fileName"]; return yourFileName; } - (void)didMoveToView:(SKView *)view { self.scaleMode = SKSceneScaleModeResizeFill; // Verify that the generateTexture method indeed produces a valid texture. SKSpriteNode *s1 = [SKSpriteNode spriteNodeWithTexture:[self generateTexture]]; s1.position = CGPointMake(100, 100); [self addChild:s1]; // Start with saving the texture. NSString *fullName = [self fullDocumentsPath]; NSError *error; NSFileManager *fileMgr = [NSFileManager defaultManager]; if ([fileMgr fileExistsAtPath:fullName]) { [fileMgr removeItemAtPath:fullName error:&error]; assert(error == nil); } NSDictionary *dict1 = [NSDictionary dictionaryWithObject:[self generateTexture] forKey:@"object"]; bool ok = [NSKeyedArchiver archiveRootObject:dict1 toFile:fullName]; assert(ok); // Read back the texture and place it in a sprite. This sprite is not shown. Why? NSData *data = [NSData dataWithContentsOfFile:fullName]; NSDictionary *dict2 = [NSKeyedUnarchiver unarchiveObjectWithData:data]; SKTexture *loadedTexture = [dict2 objectForKey:@"object"]; SKSpriteNode *s2= [SKSpriteNode spriteNodeWithTexture:loadedTexture]; NSLog(@"t(%f, %f)", loadedTexture.size.width, loadedTexture.size.height); // Size of sprite & texture is zero. Why? s2.position = CGPointMake(200, 100); [self addChild:s2]; } @end

Actualización para Yudong:

Este podría ser un ejemplo más relevante, pero imagine que la escena consiste en 4 capas, con muchos sprites. Cuando termine el juego, quiero almacenar una imagen en miniatura de la escena final del partido. La imagen se usará como una textura en un botón. Al presionar ese botón se iniciará una película de reproducción del partido. Habrá muchos botones con imágenes de juegos antiguos, así que necesito almacenar cada imagen en el archivo.

-(SKTexture*)generateTexture { SKScene *scene = [[SKScene alloc] initWithSize:CGSizeMake(100, 100)]; SKSpriteNode *ship = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"]; ship.position = CGPointMake(50, 50); [scene addChild:ship]; SKTexture *texture = [self.view textureFromNode:scene]; NSLog(@"texture: %@", texture); return texture; }

La solución / solución:

Inspirado por el código de Russell, hice lo siguiente. ¡Funciona!

CGImageRef cgImg = texture.CGImage; SKTexture *newText = [SKTexture textureWithCGImage:cgImg];


Probé tu código en Xcode 7 y encontré texture devuelta en generateTexture was null. Esa es la razón por la que no puede cargar nada del archivo, e incluso no ha guardado nada.

Intenta usar NSLog para registrar la descripción de tu textura o sprite. Por ejemplo, agregue esta línea en generateTexture :

NSLog(@"texture: %@", texture);

Lo que obtendrás en la consola:

textura: ''(nulo)'' (300 x 300)

Y lo mismo para s1 y dict1 en tu código:

s1: nombre: ''(nulo)'' textura: [''(nulo)'' (300 x 300)] posición: {100, 100} escala: {1.00, 1.00} tamaño: {100, 100} ancla: {0.5, 0.5 } rotación: 0.00

dict1: {object = "''(null)'' (300 x 300)"; }

Puede hacer estas pruebas tanto en iOS 8 como en iOS 9 y probablemente obtenga diferentes resultados.

No estoy seguro de por qué agrega el SKShapeNode a una scene y luego guarda la textura de la scene . Una solución consiste en establecer la textura de su SKShapeNode , y su código debería funcionar bien.

shapeNode.fillTexture = [SKTexture textureWithImageNamed:@"Spaceship"]; SKTexture *texture = shapeNode.fillTexture; return texture;

Actualizar:

Es bastante molesto que textureFromNode no funcione como se esperaba en iOS 9. Intenté resolverlo por prueba y error, pero textureFromNode no textureFromNode suerte. Por lo tanto, le pregunté si consideraría hacer una instantánea de toda la pantalla y configurarla como su miniatura. Aquí está el progreso que hice hoy y espero que se inspire en él.

SKLabelNode una escena que contenía SKLabelNode y SKSpriteNode en didMoveToView . Después de hacer clic en cualquier lugar de la pantalla, se invoca la snapshot y la captura de pantalla reducida se guarda en la carpeta del documento. Usé el código aquí .

- (UIImage *)snapshot { UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0.5); [self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:YES]; UIImage *snapshotImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return snapshotImage; }

Dado que la miniatura se guarda como UIImage por lo tanto, volver a cargarla para obtener la textura del sprite debe hacerse fácilmente. Un proyecto de muestra demuestra todo el proceso y funciona tanto en iOS 8 como en 9.