用 SKPhysics(碰撞)拖动一个 SKSpriteNode(跟随你的手指动作)

Posted

技术标签:

【中文标题】用 SKPhysics(碰撞)拖动一个 SKSpriteNode(跟随你的手指动作)【英文标题】:Drag a SKSpriteNode (follow your finger movements) with SKPhysics (collision) 【发布时间】:2014-07-15 15:45:27 【问题描述】:

想要使用 SKPhysics(启用碰撞)拖动 SKSpriteNode(跟随您的手指移动)

2 个失败的解决方案:

1)

a) 解决方法:改变SKSpriteNode的位置

b) 问题:当用户快速拖动 spriteNode 穿过墙壁时,SKSpriteNode 会“神奇地”意外穿过墙壁(物理似乎不起作用)

c) 代码:

@property (nonatomic) CGPoint translationBy;

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
    UITouch *touch = [touches anyObject];
    CGPoint positionInScene = [touch locationInNode:self];
    [self selectNodeForTouch:positionInScene];


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
    UITouch *touch = [touches anyObject];
    CGPoint positionInScene = [touch locationInNode:self];
    CGPoint previousPosition = [touch previousLocationInNode:self];

    CGPoint translation = CGPointMake(positionInScene.x - previousPosition.x, positionInScene.y - previousPosition.y);

    [self panForTranslation:translation];


- (void)panForTranslation:(CGPoint)translation 
    CGPoint position = [_selectedNode position];
    if([[_selectedNode name] isEqualToString:PLAYER_NAME]) 
        [_selectedNode setPosition:CGPointMake(position.x + translation.x, position.y + translation.y)];
    

2) 在这里搜索后发现@LearnCocos2D 的advice“当你使用物理时,坚持通过力、脉冲或直接改变体速度来移动物理体。”所以我尝试了:

a) 解决方案:在精灵节点上应用脉冲。

b) 问题:即使我没有拖动精灵节点,精灵节点也会继续移动。 (表现得像一艘漂浮的船。一旦停止拖动,精灵节点将停止;一旦开始拖动,它将跟随手指。

c) 代码:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
    UITouch *touch = [touches anyObject];
    CGPoint positionInScene = [touch locationInNode:self];
    [self selectNodeForTouch:positionInScene];


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
    UITouch *touch = [touches anyObject];
    CGPoint positionInScene = [touch locationInNode:self];
    CGPoint previousPosition = [touch previousLocationInNode:self];

    CGPoint translation = CGPointMake(positionInScene.x - previousPosition.x, positionInScene.y - previousPosition.y);

    CGVector forceVector = CGVectorMake(translation.x, translation.y);
    [self.player.physicsBody applyImpulse:forceVector];
    //[self.player.physicsBody setVelocity:CGVectorMake(0, 0)];


-->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

更新:

来自评论: “i)您可以确定节点到手指位置的距离,然后设置每次更新的速度:以便下一帧它将完全位于手指的位置。ii)如果手指和节点位置相同,这将取消阻止任何移动,否则节点会在执行所有物理碰撞时粘在你的手指上,并且在被阻挡的情况下会继续在自身和手指之间推动物体。”

a) 问题:如何禁用轻弹?

b) 问题:精灵节点会随着轻微的(意外)轻弹移动到很远的地方。

c) 代码:

@property (nonatomic) CGPoint translationBy;

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
    UITouch *touch = [touches anyObject];
    CGPoint positionInScene = [touch locationInNode:self];
    CGPoint previousPosition = [touch previousLocationInNode:self];
    //    1) You could determine the distance of the node to the finger location,

    CGPoint xyTranslation = CGPointMake(positionInScene.x - previousPosition.x, positionInScene.y - previousPosition.y);
    if (fabsf(positionInScene.x - previousPosition.x) < kPlayerMovementThreshold) 
        xyTranslation.x  = 0;
    
    if (fabsf(positionInScene.y - previousPosition.y) < kPlayerMovementThreshold) 
        xyTranslation.y = 0;
    
    self.translationBy = xyTranslation;


static const CGFloat kPlayerMovementSpeed = 55.0f;

static const CGFloat kPlayerMovementThreshold = 1.0f;

-(void)update:(CFTimeInterval)currentTime
     // ii) then set the velocity every update: so that it will exactly be on the finger's position the next frame.

    CGFloat dx = self.translationBy.x*kPlayerMovementSpeed;
    CGFloat dy = self.translationBy.y*kPlayerMovementSpeed;
    CGVector velocityVector = CGVectorMake(dx,dy);
    [self.player.physicsBody setVelocity:velocityVector];

【问题讨论】:

你可以添加一个 bool should_move,在 touchesBegan 内部设置 should_move 为 true,在 touchesEnded 内部设置 should_move 为 false,现在在更新类型 if (!should_move) [self.player.physicsBody setVelocity:CGVectorMake(0 , 0)];,现在您的播放器只有在您将手指放在屏幕上时才会响应。 Opps... 我忘了说 touchesEnded 方法没有被调用,因为我的手指还在屏幕上。 (但是精灵节点仍然移动-->由冲动引起) 现在你应该处理位置之间的差异,如果 (fabsf(positionInScene.x - previousPosition.x) 您可以确定节点到手指位置的距离,然后设置每次更新的速度:以便下一帧准确位于手指位置。如果手指和节点的位置相同,这将抵消任何移动,否则节点将在执行所有物理碰撞时粘在您的手指上,并在被阻挡的情况下继续在自己和手指之间推动物体。 @JulioMontoya 你能详细说明一下吗?如在哪里设置 if (fabsf(positionInScene.x - previousPosition.x) 【参考方案1】:

我所做的是制作一个 SKNode,我们称之为“a”,并附有一个非常小的物理体。这个新节点通过 SKPhysicsJointSpring 连接到我要移动的节点,我们称之为“b”。新节点 a 跟踪我的手指运动,弹簧确保目标节点 b 跟随。因为节点 b 是使用物理定位的,所以碰撞检测按预期工作。

【讨论】:

如何移动节点a?我试图通过设置a.position 来做到这一点,但这会使a 远离b 而不会移动b。然后弹簧将通过将a 移回b 来放松,而不是像我们想要的那样将b 引导到a 的方向。似乎设置position 以某种方式绕过了物理模型。或者我弄错了。

以上是关于用 SKPhysics(碰撞)拖动一个 SKSpriteNode(跟随你的手指动作)的主要内容,如果未能解决你的问题,请参考以下文章

SpriteKit SKPhysics联系问题

在精灵套件中发生碰撞时停止对象

在 iOS 中检测拖动碰撞

请推荐一个处理可拖动元素的碰撞检测的 JQuery 插件 [关闭]

限制 jQuery 可拖动项与兄弟元素重叠/碰撞

带有碰撞检测的 jQuery 拖动