背景 Sprite 图像一直在屏幕上居中

Background Sprite Image keeps being centred on screen

我正在尝试在我当前的 GameScene 中实现可滚动的背景。这应该通过 手势识别 来完成,我已经将其用于点击和移动其他场景对象。

与我的 Google 搜索返回的几乎所有其他结果不同,我不想要无限滚动的背景。它只需要用你的手指移动,并停留在它被移动的地方。

问题: 我可以在我的场景中移动背景 SKSpriteNode,但是一旦我尝试再次移动它,它就会捕捉到中心并且您的滚动实际上变得无用。它不断地自我重置。

到目前为止,这是我移动 Sprites 的结果:

-(void)selectTouchedNode:(CGPoint)location
{
    SKSpriteNode *node = (SKSpriteNode *)[self nodeAtPoint:location];
    if ([self.selectedNode isEqual:node]){
        if (![self.selectedNode isEqual:self.background]){
           self.selectedNode = NULL;
        }
    }

    if ([node isKindOfClass:[SKLabelNode class]]){
        self.selectedNode = node.parent;
    } else {
        self.selectedNode = node;
    }
    NSLog(@"Node Selected: %@ | Position: %f, %f",node.name,node.position.x,node.position.y);

}

- (void)respondToPan:(UIPanGestureRecognizer *)recognizer {

    if (recognizer.state == UIGestureRecognizerStateBegan) {
        // Get Touch Location in the View
        CGPoint touchLocation = [recognizer locationInView:recognizer.view];
        // Convert that Touch Location
        touchLocation = [self convertPointFromView:touchLocation];
        // Select Node at said Location.
        [self selectTouchedNode:touchLocation];


    } else if (recognizer.state == UIGestureRecognizerStateChanged) {
        // Get the translation being performed on the sprite.
        CGPoint translation = [recognizer translationInView:recognizer.view];
        // Copy to another CGPoint
        translation = CGPointMake(translation.x, -translation.y);
        // Translate the currently selected object
        [self translateMotion:recognizer Translation:translation];
        // Reset translation to zero.
        [recognizer setTranslation:CGPointZero inView:recognizer.view];

    } else if (recognizer.state == UIGestureRecognizerStateEnded) {
        // Fetch Current Location in View
        CGPoint touchLocation = [recognizer locationInView:recognizer.view];
        // Convert to location in game.
        CGPoint correctLocation = [self convertPointFromView:touchLocation];

        // If the selected node is the background node
        if ([self.selectedNode isEqual:self.background]) {
            NSLog(@"Scrolling the background: Node is: %@",self.selectedNode.name);
            // Set up a scroll duration
            float scrollDuration = 0.2;
            // Get the new position based on what is allowed by the function
            CGPoint newPos = [self backgroundPanPos:correctLocation];

            NSLog(@"New Position: %f, %f",newPos.x,newPos.y);

            // Remove all Actions from the background
            [_selectedNode removeAllActions];
            // Move the background to the new position with defined duration.
            SKAction *moveTo = [SKAction moveTo:newPos duration:scrollDuration];
            // SetTimingMode for a smoother transition
            [moveTo setTimingMode:SKActionTimingEaseOut];
            // Run the action
            [_selectedNode runAction:moveTo];
        } else {
            // Otherwise, just put the damn node where the touch occured.
            self.selectedNode.position = correctLocation;
        }

        }
}

// NEW PLAN: Kill myself
- (void)translateMotion:(UIGestureRecognizer *)recognizer Translation:(CGPoint)translation {

    // Fetch Location being touched
    CGPoint touchLocation = [recognizer locationInView:recognizer.view];
    // Convert to place in View
    CGPoint location = [self convertPointFromView:touchLocation];
    // Set node to that location
    self.selectedNode.position = location;
}

- (CGPoint)backgroundPanPos:(CGPoint)newPos {
    // Create a new point based on the touched location
    CGPoint correctedPos = newPos;

    return correctedPos;
}

到目前为止我知道什么?

我试过打印滚动前、结束时以及再次启动时的位置。

结果是背景 确实 移动位置,一旦您尝试再次移动它,它就会从那些新坐标开始,屏幕刚刚 重新定位 本身在精灵的中心。

配套插图:

我不确定我是否 100% 理解您所描述的情况,但我相信这可能与您的背景精灵节点的锚点有关。

在你的方法中:

- (void)translateMotion:(UIGestureRecognizer *)recognizer Translation:(CGPoint)translation

你有这条线:

self.selectedNode.position = location;

由于默认情况下背景精灵的锚点设置为其中心,因此每次移动新触摸时,它都会将背景精灵的中心捕捉到手指的位置。

换句话说,background.sprite.position设置背景精灵的锚点的坐标(默认为中心精灵的),在这种情况下,任何时候你设置一个新位置,它都会将中心移动到那个位置。

这种情况下的解决方案是每次都将背景精灵的锚点移动到触摸的正下方,这样你就可以改变背景相对于触摸开始的背景点的位置.

有点难以解释,所以这里有一些示例代码向您展示:

1) 使用 Sprite Kit 游戏模板在 Xcode 中创建一个新项目

2) 将 GameScene.m 的内容替换为以下内容:

#import "GameScene.h"

@interface GameScene ()

@property (nonatomic, strong) SKSpriteNode *sprite;

@end

@implementation GameScene

-(void)didMoveToView:(SKView *)view {
    /* Setup your scene here */
    self.sprite = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"];

    self.sprite.position = CGPointMake(CGRectGetMidX(self.frame),
                                   CGRectGetMidY(self.frame));

    [self addChild:self.sprite];
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    UITouch *touch = [touches anyObject];

    CGPoint nodelocation = [touch locationInNode:self.sprite];
    //[self adjustAnchorPointForSprite:self.sprite toLocation:nodelocation];

    CGPoint location = [touch locationInNode:self];
    self.sprite.position = location;
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    self.sprite.position = location;
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

}

-(void)adjustAnchorPointForSprite:(SKSpriteNode *)sprite toLocation:(CGPoint)location {

    // Remember the sprite's current position
    CGPoint originalPosition = sprite.position;

    // Convert the coordinates of the passed-in location to be relative to the bottom left of the sprite (instead of its current anchor point)
    CGPoint adjustedNodeLocation = CGPointMake(sprite.anchorPoint.x * sprite.size.width + location.x, sprite.anchorPoint.y * sprite.size.height + location.y);

    // Move the anchor point of the sprite to match the passed-in location
    sprite.anchorPoint = CGPointMake(adjustedNodeLocation.x / self.sprite.size.width, adjustedNodeLocation.y / self.sprite.size.height);

    //  Undo any change of position caused by moving the anchor point
    self.sprite.position = CGPointMake(sprite.position.x - (sprite.position.x - originalPosition.x), sprite.position.y - (sprite.position.y - originalPosition.y));
}

-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
}

@end  

3) 运行 sim 卡或设备上的项目,然后单击/触摸 space 船的顶端并开始拖动。

4) 请注意,spaceship 会将其中心点捕捉到您触摸的位置。如果您将它拖动并释放到新位置,然后再次触摸屏幕,它的中心就会回到您的手指。

5) 现在取消注释行:

[self adjustAnchorPointForSprite:self.sprite toLocation:nodelocation];

和 运行 项目。

6) 看看现在如何将船拖到您想要的位置,当您稍后触摸它时,它会保持原位并从您触摸它的位置开始跟随您的手指。这是因为现在每次开始新的触摸时,锚点都会调整到触摸下的点。

希望这能为您提供一个可以在您的游戏中使用的解决方案。