【问题标题】:How to lower a UIView element in a SpriteKit scene如何在 SpriteKit 场景中降低 UIView 元素
【发布时间】:2023-04-03 05:00:01
【问题描述】:

我有一个常规的 SpriteKit 场景。事实上,它是由“Sprite Kit”创建的默认场景,用于绘制宇宙飞船。

我想在背景上画画。

我已经成功添加了一个 UIImageView 作为主 skView 的子视图。我可以用线条之类的东西来绘制它。

但我似乎无法成功地将 UIImageView 放在主 skview 后面。

我该怎么做?这是我的示例代码。

(在 UIImageView 中,我只是每 0.1 秒绘制一次随机线。)

@implementation GameViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Configure the view.
    SKView * skView = (SKView *)self.view;
//    skView.showsFPS = YES;
//    skView.showsNodeCount = YES;
    /* Sprite Kit applies additional optimizations to improve rendering performance */
    //skView.ignoresSiblingOrder = YES;

    // Create and configure the scene.
    GameScene *scene = [GameScene unarchiveFromFile:@"GameScene"];
    scene.scaleMode = SKSceneScaleModeAspectFill;

    [skView presentScene:scene];
}

-(void)viewWillLayoutSubviews{

    self.imageView = [UIImageView new];
    self.imageView.frame = self.view.frame;
    [self.view addSubview:self.imageView];
    [self startRepeatingTimer];
}

-(void)viewDidLayoutSubviews{

    // this doesn't work. was just trying it out.
    int i = [[self.view subviews] count];
    [self.view exchangeSubviewAtIndex:0 withSubviewAtIndex:i-1];

}

///// DRAW RANDOM LINES EVERY 0.1 SECONDS ON THE UIIMAGEVIEW SUBVIEW
- (IBAction)startRepeatingTimer {

    [self.repeatingTimer invalidate];
    self.repeatingTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(drawOnImage:) userInfo:nil repeats:YES];
}

- (void)drawOnImage:(NSTimer*)theTimer {

    //setup
    float w = self.view.frame.size.width;
    float h = self.view.frame.size.height;

    UIGraphicsBeginImageContextWithOptions(self.view.frame.size,NO,0.0);
    [self.imageView.image drawInRect:CGRectMake(0, 0, w, h)];
    CGContextRef c = UIGraphicsGetCurrentContext();

    //draw
    CGContextMoveToPoint(c, drand48()*w,drand48()*h);
    CGContextAddLineToPoint(c, drand48()*w,drand48()*h);
    CGContextSetLineCap(c, kCGLineCapRound);
    CGContextSetLineWidth(c, 1.0f );
    CGContextSetRGBStrokeColor(c, drand48(), drand48(), drand48(), 1.0);
    CGContextStrokePath(c);

    // set the image
    self.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
}

【问题讨论】:

  • 不要使用 uiview,使用 sprite kit 节点
  • 我应该提到我的第一次尝试是使用精灵套件节点。以每秒 10 个的速率添加一个 sprite kit 节点会迅速将帧速率拖到 3fps。所以我放弃了核心图形解决方案的计划。
  • 你是在设备还是模拟器上运行的? sim 卡不是确定游戏运行速度的可靠方法。此外,即使在模拟器上,我也没有看到您使用精灵节点方法遇到的帧速率下降。
  • 使用 SpriteKit 方法至少有两个优点:1) 当你暂停游戏时它会适当地暂停,2) 当你转换到新场景时它会自动从视图中删除节点。您将需要手动暂停/删除子视图并适当地使 NSTimer 无效。
  • 我的程序的本质是无限画线,所以 skspritenodes 没有用。它最终会放缓。我同意 sprite kit 是比在 uiviews 中混合更好的解决方案。但我不知道该怎么做。 0x141E 下面的解决方案效果很好。

标签: objective-c uiview uiimageview sprite-kit skview


【解决方案1】:

这是一种在背景图像上绘制随机线条的 SpriteKit 友好方式。基本步骤是

  1. 创建空白图像
  2. 将图像转换为 SKTexture
  3. 使用纹理创建精灵
  4. 在图像上随机画一条线
  5. 使用图像更新精灵的纹理
  6. 等待 0.1 秒
  7. 迭代步骤 4-6

在 GameScene.m 中,添加以下内容

-(void)didMoveToView:(SKView *)view {

    self.scaleMode = SKSceneScaleModeResizeFill;

    // 1. Create an empty UIImage
    self.image = [self blankImageWithSize:view.frame.size];
    // 2. Convert the image to a texture
    SKTexture *texture = [SKTexture textureWithImage:_image];
    // 3. Create a sprite from the texture
    SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:texture];

    // Center the sprite in the view
    sprite.position = CGPointMake(CGRectGetMidX(view.frame), CGRectGetMidY(view..frame));
    // Set zPosition such that the sprite is underneath other nodes
    sprite.zPosition = -1000;
    [self addChild:sprite];

    self.sprite = sprite;

    // 4. and 5. Create an SKAction that draw lines on the image and then update the
    // sprite's texture
    SKAction *drawAction = [SKAction runBlock:^{
        [self drawOnImage];
        _sprite.texture = [SKTexture textureWithImage:_image];
    }];
    // 6. create a 0.1 second delay
    SKAction *wait = [SKAction waitForDuration:0.1];
    SKAction *action = [SKAction sequence:@[drawAction,wait]];

    // 7. Iterate
    [self runAction:[SKAction repeatActionForever:action]];

    // Create a label and add it to the scene to show the image is
    // in the background
    SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];

    myLabel.text = @"Hello, World!";
    myLabel.fontSize = 65;
    myLabel.position = CGPointMake(CGRectGetMidX(view.frame), CGRectGetMidY(view..frame));

    [self addChild:myLabel];

}

此方法创建一个空白图像

- (UIImage*) blankImageWithSize:(CGSize)size
{
    UIGraphicsBeginImageContext(size);
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

此方法在图像上画线

- (void)drawOnImage {

    //setup
    float w = _image.size.width;
    float h = _image.size.height;

    UIGraphicsBeginImageContextWithOptions(_image.size,NO,0.0);
    [_image drawInRect:CGRectMake(0, 0, w, h)];
    CGContextRef c = UIGraphicsGetCurrentContext();

    //draw
    CGContextMoveToPoint(c, drand48()*w,drand48()*h);
    CGContextAddLineToPoint(c, drand48()*w,drand48()*h);
    CGContextSetLineCap(c, kCGLineCapRound);
    CGContextSetLineWidth(c, 1.0f );
    CGContextSetRGBStrokeColor(c, drand48(), drand48(), drand48(), 1.0);
    CGContextStrokePath(c);

    // set the image
    _image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
}

【讨论】:

  • 这是正确的答案。谢谢你。我在 iphone 5 上测试了这个版本和我的旧版本。这个版本在绘制 1,000 行时比直接在 UIImageView 上绘制慢大约 30%。但是速度足以满足我的需求并且效果很好。此外,它使用 spritekit 动画,避免了停止 NSTimers 和手动停止视图的需要。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-24
  • 1970-01-01
  • 2019-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多