1

Couldn't find a solution or an answer online ...

I'm building an AR game using SceneKit. I have a roomNode which has several wallNodes as children. The whole roomNode is set to detect collisions. It all works perfectly fine. Except ...

In the game I allow the user to tap a wall to remove it, at which point I would allow the user to walk through the missing wall without a collision being detected. At least that's what I want to happen ... After identifying the node with a hitTest, I just run:

node?.removeFromParentNode()

The problem is that I see the wall correctly being removed, but collisions continue to occur at the location where the wallNode used to be and now it's just empty space.

When I turn on debugging to show physics shapes and feature points like so:

sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints, ARSCNDebugOptions.showWorldOrigin, SCNDebugOptions.showPhysicsShapes]

after removing the wall, I still see the outline of that removed wall as if it's still there. Like a ghost of the wall still exists and it's still part of the higher level roomNode which still detects collisions.

Here are different things that I tried to solve this issue:

  1. I saw in some other threads that after setting the node's geometry or its different firstMaterials contents to nil it would free some memory that was kept so I also tried doing that, but it didn't change anything.

  2. I also tried setting the node itself to nil, but that didn't help either.

  3. I tried changing that node's physicsBody.type = .kinematic so that even if a ghost remains in my scene, at least collisions wouldn't move the node, but that didn't work either.

  4. I also tried changing the node's physicsBody different collision bitmasks to something that shouldn't cause a collision, but still no luck.

  5. I tried to run physicsBody.resetTransform() for both the roomNode and for the node that was removed and that also did nothing.

  6. The only thing that actually removed the ghost completely and allowed me to walk through the removed wallNode was if I recreated the whole roomNode.physicsBody like that:

    roomNode?.physicsBody = SCNPhysicsBody(type: .dynamic, shape: SCNPhysicsShape(node: roomNode!, options: nil))

So that did the trick. But then, if my roomNode was previously moved due to collisions or if I moved the roomNode myself in code (using a pivot to reorient the room for the game's purposes), after recreating the whole physicsBody, the whole roomNode would redraw in a different location. I can now try to figure out how to compensate for any node movement, but that can become very tricky if at all possible.

But there should be a correct way to remove some tapped node so that it would get completely removed from the scene without leaving any ghosts.

Don't know if this has any relevance, but the roomNode is defined globally as a property of my ViewController. The different wallNodes on the other hand, are each created elsewhere locally and then added as child nodes.

If anyone knows how I could resolve this or if you have any clue I could try, please let me know. Thanks!

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
rafi
  • 389
  • 2
  • 10

1 Answers1

1

This definitely does the trick:

node?.physicsBody = nil
node?.removeFromParentNode()

.physicsBody instance property is optional:

var physicsBody: SKPhysicsBody? { get set }

Also, make sure that the removed node, not its parent node, had a physics enabled.

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • 1
    thanks! This does make sense. My problem is that I need to set the physicsBody to the parent roomNode because if the user collides with any of the walls, I need to move the whole room as one. If I set physicsBody individually to each wallNode then a collision will cause that specific wall to move and break the room as the rest of the walls won't move along. So now I understand that when I remove a wallNode from the parent, it gets removed, but the physicsBody for the whole parent roomNode remains, even for the removed wall, until I recreate the whole parent's .physicsBody. – rafi Jul 23 '22 at 18:25
  • If there is any other way I could move the whole room node when colliding with a wall when the physicsBody is set for the individual walls, I'd love to know how to do that. Thanks! – rafi Jul 24 '22 at 08:30
  • Please describe what is the purpose of this app and what you're trying to achieve. – Andy Jazz Jul 24 '22 at 08:34
  • 1
    thanks. I cannot disclose everything, but I want the user to walk around the room without crossing through any of the walls and if the user hits a wall, I want the whole room to move with the user for that purpose. So the room will remain static relative to the user if he tries to go through a wall. For that, I need the whole room to move as one. I have a cylinder node with 0 opacity which is always at pointOfView which represents the user and I check collisions between that cylinder node and the roomNode. Thanks! – rafi Jul 24 '22 at 13:31
  • In other words you need a `.kinematic` mode like this – https://stackoverflow.com/questions/61753464/how-to-prevent-entities-from-overlapping/71230104#71230104. In SceneKit, it's a `kinematic()` type method – https://developer.apple.com/documentation/scenekit/scnphysicsbody/1514776-kinematic. So, a character is `.kinematic`, walls – `.dynamic` (collisions must be enabled). – Andy Jazz Jul 24 '22 at 13:50
  • Collision shape in this case should be monolithic (not divided into separate parts). – Andy Jazz Jul 24 '22 at 14:02
  • Yes that is exactly what I did. The cylinder I use as the user is kinematic and the roomNode is dynamic and I set the physcsBody to the whole roomNode. So now we're back to my original problem: if I remove a single wallNode, it gets removed, but the physcsBody for that wall as part of the whole room remains and I can't walk through it. Thanks! – rafi Jul 24 '22 at 14:53
  • I don't see any problem - use a mono-object (whole-room-walls), do not use separate walls. – Andy Jazz Jul 24 '22 at 15:36
  • thanks again for getting back. Not sure I understand the last comment. I am giving the physicsBody to the whole roomNode as a monolithic node and all collisions happen with the whole room as a whole. But then in the game I need to remove a single wall and here after removing that wall from its parent, the physicsBody of the whole room still has that wall and I continue to collide with the removed wall. – rafi Jul 25 '22 at 15:55