SpriteKit 中的缩放和滚动 SKNode [英] Zoom and Scroll SKNode in SpriteKit

查看:24
本文介绍了SpriteKit 中的缩放和滚动 SKNode的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在 SpriteKit 上开发像 Scrabble 这样的游戏,并且一直坚持缩放和滚动 Scrabble Board.首先让我解释一下游戏背后的工作:在我的 GameScene 上:

I am working on a Game like Scrabble on SpriteKit and have been stuck on Zooming and Scrolling the Scrabble Board. First Let me Explain the working behind the game: On my GameScene I Have:

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

  • A SKNode subclass called GameBoard Layer (named NAME_GAME_BOARD_LAYER) containing following Children:

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

从拼图架上挑选字母拼图,然后放到拼字游戏板上.

The Letters Tiles are picked from the Tile Rack and dropped at the Scrabble Board.

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

The problem here is, I need to mimic the zooming and scrolling which can be achieved by UIScrollView, which I think cant be added on a SKNode. The Features I need to mimic are:

  • 放大用户双击的精确位置
  • 滚动(尝试过 PanGestures,不知何故会导致图块拖放问题)
  • 将缩放后的 SKNode 保持在特定区域中(就像 UIScrollView 将缩放后的内容保持在 scrollView 边界内)

这是我使用 UITapGestures 进行缩放的代码:

Here is the Code I have used for Zooming, using UITapGestures:

在我的 GameScene.m 中

In my 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 节点中:

In my GameBoardLayer Node:

- (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]];
}
}

有人能指导我如何实现此功能吗?

Would someone kindly guide me how can i achieve this functionality?

谢谢大家.

推荐答案

我会这样做:

设置:

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

保持相机节点居中:

// 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 来平移视图(记住要防止平移越界)

Pan view by moving BoardNode around (Remember to prevent panning out of bounds)

// 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 的大小来缩放:

Pinch Zoom by setting the size of your 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 决定哪些节点应该响应触摸(以及以什么顺序).

What comes to the problem of panning accidentally dragging your LetterNodes, I usually implement a single TouchDispatcher (usually in GameScene class) that registers all the touches. TouchDispatcher then decides which node(s) should respond to the touch (and in which order).

这篇关于SpriteKit 中的缩放和滚动 SKNode的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆