在 SpriteKit 中移动相机

Posted

技术标签:

【中文标题】在 SpriteKit 中移动相机【英文标题】:Moving a camera in SpriteKit 【发布时间】:2014-05-25 01:46:10 【问题描述】:

//更新 添加了更新的代码,可以按我的预期工作。请参阅下面更新代码中的 didSimulatePhysics 方法。就我而言,我只关心在 x 轴上向左或向右移动字符,其中 x 轴上的 0 是绝对左侧,而 x 轴上的右侧是可配置值。 Apple 冒险游戏也确实帮了很多忙。

//下面是原帖

我正在使用 Apple SpriteKit,并且我正在努力实现我希望它表现的相机。我在代码中所做的是加载一个精灵字符、两个按钮和一个红色框,该框在开始时位于视图右侧。我想做的是用按钮移动角色,一旦玩家到达屏幕的中间或末端,相机就会重新调整以发现在视图中看不到的东西。因此,一旦玩家到达那里,向右移动最终应该会显示最初在视图之外关闭的红色框。但是,使用我在下面使用的代码,我根本无法让相机跟随并调整主角的坐标。我查看了 Apple 的高级场景处理文档以及其他一些堆栈溢出帖子,但似乎无法正确理解。如果有人可以提供一些建议,将不胜感激。

#define cameraEdge 150

-(id)initWithSize:(CGSize)size

    if (self = [super initWithSize:size])
    
        /* Setup your scene here */
        //320 568
        
    
        self.backgroundColor = [SKColor whiteColor];
        
        myWorld = [[SKNode alloc] init];
        [self addChild:myWorld];
        
        mainCharacter = [SKSpriteNode spriteNodeWithImageNamed:@"0"];
        mainCharacter.physicsBody.dynamic = YES;
        mainCharacter.name = @"player";
       
        mainCharacter.position = CGPointMake(20, 20);
        
        CGRect totalScreenSize = CGRectMake(0, 0, 800, 320);
        
        SKSpriteNode *box = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(60, 60)];
        
          SKSpriteNode *boxTwo = [SKSpriteNode spriteNodeWithColor:[SKColor greenColor] size:CGSizeMake(60, 60)];
           SKSpriteNode *boxThree = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(60, 60)];
        boxThree.position = CGPointMake(40, 50);
        
        [myWorld addChild:boxThree];
        
        boxTwo.position = CGPointMake(1100, 50);
        
        box.position = CGPointMake(650, 50);
        
        [myWorld addChild:box];
        [myWorld addChild:boxTwo];
        
       
       self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:totalScreenSize];
        
        self.physicsWorld.gravity = CGVectorMake(0, -5);
        mainCharacter.name = @"mainCharacter";
  
   
        mainCharacter.physicsBody.linearDamping = 0;
        mainCharacter.physicsBody.friction = 0;
        mainCharacter.physicsBody.restitution = 0;
        mainCharacter.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:mainCharacter.size];
        
       [myWorld addChild:mainCharacter];
      
        [self addChild:[self buildLeftButton]];
        [self addChild:[self buildRightButton]];

    
    
    return self;


- (void)didSimulatePhysics

    SKSpriteNode *hero = mainCharacter;
    
    if(hero)
    
        CGPoint heroPosition = hero.position;
        CGPoint worldPosition = myWorld.position;
        
        NSLog(@"%f", heroPosition.x);
        
        CGFloat xCoordinate = worldPosition.x + heroPosition.x;
        
        if(xCoordinate < cameraEdge && heroPosition.x > 0)
        
            worldPosition.x = worldPosition.x - xCoordinate + cameraEdge;
            self.worldMovedForUpdate = YES;
        
        
        else if(xCoordinate > (self.frame.size.width - cameraEdge) && heroPosition.x < 2000)
        
            worldPosition.x = worldPosition.x + (self.frame.size.width - xCoordinate) - cameraEdge;
            self.worldMovedForUpdate = YES;
        
        
        myWorld.position = worldPosition;
    


-(SKSpriteNode *)buildLeftButton

    SKSpriteNode *leftButton = [SKSpriteNode spriteNodeWithImageNamed:@"left"];
    leftButton.position = CGPointMake(20, 20);
    leftButton.name = @"leftButton";
    leftButton.zPosition = 1.0;
    return leftButton;


-(SKSpriteNode *)buildRightButton

    SKSpriteNode *leftButton = [SKSpriteNode spriteNodeWithImageNamed:@"right"];
    leftButton.position = CGPointMake(60, 20);
    leftButton.name = @"rightButton";
    leftButton.zPosition = 1.0;
    return leftButton;



-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    SKNode *node = [self nodeAtPoint:location];
    
    if([node.name isEqualToString:@"leftButton"])
    
             [mainCharacter.physicsBody applyImpulse:CGVectorMake(-120, 0)];
    
    
    else if([node.name isEqualToString:@"rightButton"])
    
       [mainCharacter.physicsBody applyImpulse:CGVectorMake(120, 10)];
    

【问题讨论】:

【参考方案1】:

如果您希望视图始终以玩家的位置为中心,请记住以下几点修改您的代码:

1) 创建一个 SKNode 并将其命名为 myWorld、worldNode 或任何其他类似名称。

2) 添加世界节点[self addChild:worldNode];

3) 将所有其他节点添加到 worldNode,包括您的播放器。

4) 在didSimulatePhysics 方法中,添加以下代码:

worldNode.position = CGPointMake(-(player.position.x-(self.size.width/2)), -(player.position.y-(self.size.height/2)));

您的视图现在将始终以玩家的位置为中心。

2015 年 5 月更新:

如果您使用的是通过平铺地图编辑器创建的地图,您可以使用免费的SKAToolKit framework。功能包括播放器相机自动跟随、测试播放器、测试 HUD 和精灵按钮。

【讨论】:

感谢 sangony 的回复。我已经修改了原始帖子中的代码以反映您建议的更改。但是,该角色出现在右上角,并且移动按钮消失了。我假设我必须以某种方式将移动按钮锚定在当前视图中,以便它们在视图调整时移动,但不太确定按钮是否应该锚定到场景、世界或相机。 @zic10 - 您可以直接将控制按钮添加到视图中,这样它们就不会移动。如果您需要修改与播放器相关的中心位置,您可以添加偏移值。例如:((player.position.x-(self.size.width/2)+20) 偏移量为+20。 这是否意味着我可以丢弃相机节点而只在 myWorld 节点中执行所有操作? @zic10 - 是的。把“worldNode”想象成一个巨大的画布,你可以在你的视图前四处移动,它有点像一扇窗户。应该与 worldNode 一起移动的所有内容都应该是 worldNode 的子节点。请记住,当窗口(您的视图)保持静止时,您正在移动画布(worldNode)。 抱歉耽搁了,已接受为答案。我还想补充一点,Apple 的“冒险”游戏示例中的一些代码也确实有帮助。将在原始帖子中添加此代码。

以上是关于在 SpriteKit 中移动相机的主要内容,如果未能解决你的问题,请参考以下文章

带有 Skview 的 ViewController - SpriteKit SKScene

在 SpriteKit 项目中包含一个 UIKit 小游戏

SpriteKit 捏缩放相机

在 SpriteKit 中将相机夹在场景的背景周围

Spritekit -- 保持场景以高于特定高度的精灵为中心

Sprite Kit 相机平滑移动,延迟