我现在已经为下面的问题提交了一个错误。任何人都有好的 解决办法:
我尝试将SKTexture
保存到文件中,然后重新加载,但我没有成功。以下代码段可以复制到Xcode启动项目中的 GameScene.m 。
我在textureFromNode
中使用generateTexture
,这似乎是我问题的根本原因。如果我使用精灵中的纹理,则代码可以工作,并且可以看到两个太空飞船。
此代码在iOS 8中有效,但它在Xcode7& iOS 9.我只想在提交错误报告之前验证这是一个错误。我担心的是我对NSKeyedArchiver
做错了。
它发生在模拟器和设备上。
#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
Yudong更新:
这可能是一个更相关的例子,但想象一下场景由4层组成,有很多精灵。当游戏结束时,我想存储比赛结束场景的缩略图。该图像将用作按钮上的纹理。按下该按钮将开始比赛的重播电影。将有许多按钮与旧游戏的图像,所以我需要将每个图像存储在文件中。
-(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;
}
解决方案/解决方法:
受到Russells代码的启发,我做了以下工作。它有效!
CGImageRef cgImg = texture.CGImage;
SKTexture *newText = [SKTexture textureWithCGImage:cgImg];
答案 0 :(得分:4)
我已经使用SKTextures进行了大量的实验/黑客攻击。我的游戏使用SKTextures。它是用Swift编写的。具体来说,我在textureFromNode和textureFromNode:crop:和纹理创建SKPhysicsBodies方面遇到了很多问题。这些方法在ios 8中运行良好,但Apple在发布ios 9.0时完全破坏了它们。在ios 9.0中,纹理以零形式返回。那些零纹理从纹理中打破了SKPhysicsBodies。
我最近致力于SKTextures的序列化/反序列化。
您可能会调查的一些关键想法/线索是:
运行ios 9.2。 Apple员工提到很多问题已得到修复。 https://forums.developer.apple.com/thread/17463我发现ios 9.2对SKTextures有帮助,但没有解决每个问题,尤其是序列化问题。
尝试PrefersOpenGL(将其设置为" YES"作为配置中的布尔自定义属性)。以下是Apple Staff的Apple Dev论坛中关于PrefersOpenGL的帖子。 https://forums.developer.apple.com/thread/19683我观察到ios 9.x似乎默认使用Metal而不是OpenGL。我发现PrefersOpenGL有助于解决SKTexture问题,但仍然无法使我的SKShaders工作(用GLSL编写)。
当我尝试在ios 9.2上使用SKTextures序列化/反序列化节点时,我得到的是白色方框而不是可见纹理。灵感来自苹果SKTexture文档说,"纹理数据在以下情况下加载:
调用纹理对象上的size方法。
调用另一个需要纹理大小的方法,例如创建一个使用纹理对象的新SKSpriteNode对象。
调用其中一个预加载方法(请参阅预加载纹理数据。)
纹理数据准备在以下情况下渲染:
使用纹理的精灵或粒子是正在渲染的节点树的一部分。"
...我已经破解了一个从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())
所以现在我用这种方式创建的SKSpriteNodes似乎很好地序列化/反序列化。顺便说一句,只是调用size()或用原始纹理创建一个SKSpriteNode似乎不足以将纹理重新化为内存。
我希望这会对你有所帮助。我想,由于我的应用程序非常依赖于它,因此我使用SKTextures比大多数人更挣扎。
答案 1 :(得分:2)
我在Xcode 7中测试了您的代码,发现texture
中返回的generateTexture
为空。这就是为什么你无法从文件中加载任何东西的原因,你甚至还没有保存任何东西。
尝试使用NSLog
记录纹理或精灵的描述。例如。在generateTexture
中添加此行:
NSLog(@"texture: %@", texture);
您将在控制台中获得什么:
纹理:'(null)' (300 x 300)
代码中s1
和dict1
的内容相同:
s1:name:'(null)'纹理:['(null)' (300 x 300)]位置:{100,100}比例:{1.00,1.00}大小:{100,100} 锚:{0.5,0.5}旋转:0.00
dict1:{ object =" '(空)' (300 x 300)&#34 ;; }
您可以在iOS 8和iOS 9上进行这些测试,您可能会得到不同的结果。
我不确定为什么要将SKShapeNode
添加到scene
,然后从scene
保存纹理。一种解决方法是为SKShapeNode
设置纹理,您的代码应该可以正常工作。
shapeNode.fillTexture = [SKTexture textureWithImageNamed:@"Spaceship"];
SKTexture *texture = shapeNode.fillTexture;
return texture;
<强>更新强>
令人讨厌的是,textureFromNode
在iOS 9中没有像预期的那样发挥作用。我试图通过反复试验来解决它,但最后没有运气。因此,我问你是否考虑制作整个屏幕的快照并将其设置为缩略图。这是我今天取得的进步,希望你能从中获得灵感。
我在SKLabelNode
中创建了一个包含SKSpriteNode
和didMoveToView
的场景。点击屏幕上的任意位置后,将调用snapshot
,并将缩小的屏幕截图保存在文档文件夹中。我使用了代码here。
- (UIImage *)snapshot
{
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0.5);
[self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:YES];
UIImage *snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshotImage;
}
由于缩略图保存为UIImage
,因此应该轻松地将其加载回精灵的纹理。 A sample project演示了整个过程,它适用于iOS 8和9。