12

I can't change the position of a SKSpriteNode by changing

self.position = newPosition;

It doesn't work anymore if it has a physicsBody.

A workaround I got is:

    self.myStar.physicsBody = nil;

    [self.myStar changePosition];

    self.myStar.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.myStar.size.width/2];

Or

    [self runAction:[SKAction moveTo:newPosiion duration:0.0]];

But #2 isn't smooth. It needs to pop up on another position without a moving action.

CodeSmile
  • 64,284
  • 20
  • 132
  • 217
Norman
  • 176
  • 1
  • 7

7 Answers7

10

I had the same problem. Apparently you cannot explicitly set the position of a Sprite node once it has a PhysicsBody. I solved it by temporarily removing the sprite node's PhysicsBody.

CGFloat yPosition = 400.f;
SKPhysicsBody* goldfishPhysicsBody = _goldfish.physicsBody;
_goldfish.physicsBody = nil;
_goldfish.position = CGPointMake(_goldfish.position.x, yPosition);
_goldfish.physicsBody = goldfishPhysicsBody;
user3440752
  • 101
  • 1
  • 3
4

Yes, you can!

I'm not sure what exactly you're doing and where exactly you run this code and what the observed effect is that you meant by "can't change position" but changing a node's position always works, whether the node has a physicsBody or not, and whether the physicsBody is dynamic or static. I verified this in a simple test project with dynamic set to both YES and NO, with both circle and edge loop body types, using both position property and move actions.

You can change the node's position either by setting the position property directly or by using a move action - both variants work. If you use a 0 duration for the move action, it effectively becomes the same as setting the position property.

So whatever problem you're observing, it's not because you can't generally change a node's position once it has a physicsBody. That's absolutely not the case.

I'm guessing you may have run into one of the following problems:

  • node is already running a different move action, overriding your position changes
  • you were looking at the wrong node (use logs to verify actual position of the node in question)
  • you change position elsewhere, for example setting the node's position every frame thus invalidating any other position change
  • if it's not one of the above, then possibly something else I couldn't think of ...
CodeSmile
  • 64,284
  • 20
  • 132
  • 217
  • Well at me setting the `position` property direct didn't worked (the node just kept there old position). With an SKAction it worked well... – idmean Jan 28 '14 at 16:11
  • 6
    I've found that if you set the `position` inside a contact handler, there is no effect. The physics simulation clobbers it right away. I deferred setting it until the `update:` method and it worked. – bugloaf Jan 03 '15 at 15:57
  • 1
    This is indeed an issue and it seems you cannot directly change position for nodes with physicsbody as mention in question. I ended up going the SKaction route. – Kashif Feb 16 '16 at 18:25
  • @LearnCocos2D, I was trying to change position of a node in update. That is how I get new location from. I assume this falls under "setting the node's position every frame". What would you suggest doing in my case? Thank you! – Vetuka Jan 11 '19 at 22:48
3

I think the problem you are having is that in order for you to control a phsyics body, you need to set it to dynamic.

self.myStar.physicsBody.dynamic = NO;

If the dynamic property is set to YES, then the physics engine is in control of it's movement.

As noted in the comments, setting dynamic to NO shouldn't restrict movement via SKAction or position property. However, if it is set to YES, it's possible that something in the physics engine is affecting your attempt to move the node.

If you want the movement to not pop, then you need to set a duration higher than zero to your SKAction or it will pop as you have described. Setting duration to 0.0 is the same as just changing the position.

For example :

[self runAction:[SKAction moveTo:newPosition duration:1.0]];

will move the node to the new position over 1 second.

prototypical
  • 6,731
  • 3
  • 24
  • 34
  • 3
    The part about dynamic YES/NO is false. You can always change the position of a body by action or setting node.position, whether dynamic or not. – CodeSmile Jan 05 '14 at 19:57
  • 1
    The point is that if there is something in the physics world that is affecting it's movement, setting to YES will remove that case. It's possible that is what is in play here. I haven't actually tried to control a physicsBody via SKAction or otherwise if using physics. So that is good to know, regardless. – prototypical Jan 05 '14 at 22:22
  • Ah that's what you meant. If it's a static body it won't be moved by the physics simulation. – CodeSmile Jan 05 '14 at 23:09
  • 1
    @ prototypical - That is still the opposite of what dynamic = true does. When dynamic the object reacts to the physics system, when static it contributes but does not react. – rebusB Jun 06 '16 at 18:40
  • @rebusB Thanks, I had it flipped. Point is the same though, if physics is trying to control the body while you are via SKAction something of this nature could happen. – prototypical Jun 07 '16 at 19:41
3

I had a problem like this where I tried to update position inside didBeginContact:, but it was not having any effect, I think because the physics simulation immediately clobbers the value.

My solution was to cache the new position in a property on my scene and then update the position of the node next time update: was called on my scene.

[SKAction moveTo:newPosition duration:0.0] worked for me, too, and there was no popping. I haven't decided yet which is more elegant.

bugloaf
  • 2,890
  • 3
  • 30
  • 49
1

I ran into a similar problem. When using SKAction, even with duration set to 0.0 I got strange behaviours especially when two SKActions had been triggered at the same time.

I tried setting position directly but as mentioned by others this doesn't work when using the SKPhysicsContactDelegate.

However for me it worked to remove the node from its parent, I then set the new position, and other things I want to change, and then I add the node again to its former parent.

It's not ideal but in some cases it might help.

As an example with the SKPhysicsContactDelegate method didBegin:

func didBegin(_ contact: SKPhysicsContact) {

    guard let node = contact.bodyB.node else { return }

    node.removeFromParent()
    node.position = CGPoint(x: 10, y: 10)
    addChild(node)
}
Marco Boerner
  • 1,243
  • 1
  • 11
  • 34
0

Seems similar to SKSPriteNode position changes to 0,0 for no reason

As stated in answer and comments of this question, it seems you must either set the position before setting the physicsBody and/or set the physicsBody after adding the node to your scene.

I've had this problem in several scenarios and I haven't found out what exactly causes it to fail sometimes.

Community
  • 1
  • 1
Koop
  • 401
  • 2
  • 5
  • 10
0

Yes you can, but it has its price.

I think you have to make a decision: Either let the position being calculated by the PhysicsEngine OR set the position on your behalf.

Look, for an object in a physics world, there is no magical movement from 'outside', there is only forces, collisions, drifts, ... and that will lead to any position. You can not manipulate the position without putting forces on some related nodes.

And in the moment you try BOTH, having a physicsBody (e.g. on your player), but also try to move them around by setting position manually or running actions for moving, YOU LEAVE the world of physics. Your player will be MOVED through walls and so on, against all physics rules.

So, to want an object being manipulated by the physics engine on the one hand and also to want "positioning" by code is kind a contradiction.

There are - of course ways - to combine both ways (e.g. the mentioned 'workaround' by temporarily removing the physicsBody), but this has also its price and has to be sequentially. You can not have both ways at the very same time.

LukeSideWalker
  • 7,399
  • 2
  • 37
  • 45