在SpriteKit中缩放和滚动SKNode

我正在制作像SpriteKit上的Scrabble这样的游戏,并且一直停留在Scoble和Scrolling Scrabble Board上。 首先让我解释游戏背后的工作:在我的GameScene上我有:

  • 一个名为GameBoard Layer(名为NAME_GAME_BOARD_LAYER)的SKNode子类,包含以下子项:

    A SKNode subclass for Scrabble Board named NAME_BOARD. A SKNode subclass for Letters Tile Rack named NAME_RACK. 

    Letters Tiles从Tile Rack中挑选出来并放在Scrabble Board上。

这里的问题是,我需要模仿UIScrollView可以实现的缩放和滚动,我认为不能在SKNode上添加。 我需要模仿的function是:

  • 缩放用户双击的精确位置
  • 滚动(尝试PanGestures,以某种方式创建拖拽瓦片的问题)
  • 将Zoomed SKNode保留在特定区域中(与UIScrollView一样,将缩放的内容保留在scrollView边界内)

这是我用于缩放的代码,使用UITapGestures:

在我的GameScene.m中

 - (void)didMoveToView:(SKView *)view { UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; tapGesture.numberOfTapsRequired = 2; tapGesture.numberOfTouchesRequired = 1; [self.scene.view addGestureRecognizer:tapGesture]; } - (void)handleTapGesture:(UITapGestureRecognizer*)recognizer { if ([self childNodeWithName:NAME_GAME_BOARD_LAYER]) { GameBoardLayer *gameBoardLayer = (GameBoardLayer*)[self childNodeWithName:NAME_GAME_BOARD_LAYER]; SKNode *node = [Utils nodeAt:[recognizer locationInView:self.view] withName:NAME_BOARD inCurrentNode:gameBoardLayer]; if ([node.name isEqualToString:NAME_BOARD]) { [gameBoardLayer handleDoubleTap:recognizer]; } } } 

在我的GameBoardLayer节点中:

 - (void)handleDoubleTap:(UITapGestureRecognizer*)recognizer { Board *board = (Board*)[self childNodeWithName:NAME_BOARD]; if (isBoardZoomed) { [board runAction:[SKAction scaleTo:1.0f duration:0.25f]]; isBoardZoomed = NO; } else { isBoardZoomed = YES; [board runAction:[SKAction scaleTo:1.5f duration:0.25f]]; } } 

有人会指导我如何实现这一function?

感谢大家。

这就是我要这样做的方式:

建立:

  • 创建一个GameScene作为游戏的rootNode。 (SKScene的孩子)
  • 将BoardNode作为子节点添加到场景中(SKNode的子节点)
  • 将CameraNode作为子节点添加到Board(SKNode的子节点)
  • 添加LetterNodes作为董事会的子女

保持Camera节点居中:

 // GameScene.m - (void) didSimulatePhysics { [super didSimulatePhysics]; [self centerOnNode:self.Board.Camera]; } - (void) centerOnNode:(SKNode*)node { CGPoint posInScene = [node.scene convertPoint:node.position fromNode:node.parent]; node.parent.position = CGPointMake(node.parent.position.x - posInScene.x, node.parent.position.y - posInScene.y); } 

通过移动BoardNode来平移视图(记住防止平移越界)

 // GameScene.m - (void) handlePan:(UIPanGestureRecognizer *)pan { if (pan.state == UIGestureRecognizerStateChanged) { [self.Board.Camera moveCamera:CGVectorMake([pan translationInView:pan.view].x, [pan translationInView:pan.view].y)]; } } // CameraNode.m - (void) moveCamera:(CGVector)direction { self.direction = direction; } - (void) update:(CFTimeInterval)dt { if (ABS(self.direction.dx) > 0 || ABS(self.direction.dy) > 0) { float dx = self.direction.dx - self.direction.dx/20; float dy = self.direction.dy - self.direction.dy/20; if (ABS(dx) < 1.0f && ABS(dy) < 1.0f) { dx = 0.0; dy = 0.0; } self.direction = CGVectorMake(dx, dy); self.Board.position = CGPointMake(self.position.x - self.direction.dx, self.position.y + self.direction.dy); } } // BoardNode.m - (void) setPosition:(CGPoint)position { CGRect bounds = CGRectMake(-boardSize.width/2, -boardSize.height/2, boardSize.width, boardSize.height); self.position = CGPointMake( MAX(bounds.origin.x, MIN(bounds.origin.x + bounds.size.width, position.x)), MAX(bounds.origin.y, MIN(bounds.origin.y + bounds.size.height, position.y))); } 

通过设置GameScene的大小来缩放:

 // GameScene.m - (void) didMoveToView:(SKView*)view { self.scaleMode = SKSceneScaleModeAspectFill; } - (void) handlePinch:(UIPinchGestureRecognizer *)pinch { switch (pinch.state) { case UIGestureRecognizerStateBegan: { self.origPoint = [self GetGesture:pinch LocationInNode:self.Board]; self.lastScale = pinch.scale; } break; case UIGestureRecognizerStateChanged: { CGPoint pinchPoint = [self GetGesture:pinch LocationInNode:self.Board]; float scale = 1 - (self.lastScale - pinch.scale); float newWidth = MAX(kMinSceneWidth, MIN(kMaxSceneWidth, self.size.width / scale)); float newHeight = MAX(kMinSceneHeight, MIN(kMaxSceneHeight, self.size.height / scale)); [self.gameScene setSize:CGSizeMake(newWidth, newHeight)]; self.lastScale = pinch.scale; } break; default: break; } } 

不小心拖动你的LetterNodes的问题是什么,我通常会实现一个TouchDispatcher(通常在GameScene类中)来记录所有的触摸。 然后,TouchDispatcher决定哪个节点应该响应触摸(以及以哪种顺序)。