SpriteKit: detect complete node overlap - ios

I have two SKShapeNodes – one with an edge-based SKPhysicsBody, one volume-based – and I want to detect their intersection without collision. I've got this working fine, with the SKPhysicsContactDelegate contact methods getting called as one passes over another, but my issue is that didEndContact gets called when the edges no longer intersect, even when one body is completely contained within the other. What's the best way to determine true contact or overlap, not just edge intersection? I've tried usesPreciseCollisionDetection, to no avail.

CGPoint locObj1 = [sprite1 locationInNode:self];
CGPoint locObj2 = [sprite2 locationInNode:self];
if([sprite1 containsPoint: locObj2]) return;
if([sprite2 containsPoint: locObj1]) return;
Add this to the beginning of didBeginContact and didEndContact. This checks to see if one of the nodes contains the other node. If it does, it does nothing which will alleviate your issue of didBeginContact and didEndContact being unessesarily called. I am not on my mac so you may need to play with the syntax a bit. Hope this sends you in the right direction.

As meisenman suggested, it looks like the best way to do this is using the containsPoint method to determine true node overlap. The docs state that this "returns a Boolean value that indicates whether a point lies inside the node's bounding box", but in my experimentation it looks like this works for concave edge-based shapes as well.
So if I want to detect when two objects no longer overlap, it makes sense to do a containsPoint check within didEndContact – but it turns out didEndContact gets called when each edge is no longer intersected, even if another edge of the same shape still intersects the same object. For example, a ball exiting a rectangle through its corner will yield two didEndContact events. Therefore, it's necessary to keep an absolute count of contact events (the difference between begin and end), and only test for containment when this count is zero.
My solution, in Swift:
var _contactCount = 0
func didBeginContact(contact: SKPhysicsContact!) {
if ((contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask) == (CATEGORY_ONE | CATEGORY_TWO)) {
if (_contactCount == 0) {
// Contact is actually beginning
}
_contactCount += 1
}
}
func didEndContact(contact: SKPhysicsContact!) {
if ((contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask) == (CATEGORY_ONE | CATEGORY_TWO)) {
_contactCount -= 1
let overlap = contact.bodyA.node.containsPoint(contact.bodyB.node.position) || contact.bodyB.node.containsPoint(contact.bodyA.node.position)
if (!overlap && _contactCount == 0) {
// Contact is actually ending
}
}
}

These are my thoughts on #meisenman example for swift 3. Instead of collision detection through masks, let's say we wanted to to know if an node is inside a node. With #meisenman, the location of the node is being used for each.
This following image is what we actually what I want done.
The code #meisenman uses does not require SKPhysics. Here is the code in Swift 3.0
let Object_1: CGPoint! = Sprite_Object_1.position
//this obtains the location of the sprite node, through CGPoint.
if Object_Sprite_2.contains(Object_1){
print("Then it is true, object 2 is within the location of object 1")
}
The only limitations that I have found are that if the center of these nodes are not within the body of the desired node, then this if statement will not be true. This has not been tested by setting the node's body defination (mask).
What I like, is having the ability to use this if statement throughout different functions or overide fuctions, this is not limited to only 'did begin' or 'touches moved.'

Related

Is there a simple way to temporarily disable the physics bodies for a sprite in Swift? (SpriteKit)

I'm making a maze app, that generates a new maze after one is completed. Obviously, I can turn the walls (each having their own physics and sprite stored in their spot on an array) invisible by doing what I did in my code wall[location].isHidden = true ,but I couldn't find a way to do the same with the physics body bound to the wall, creating an invisible wall. Is there a simple way to do that without setting it to nil?
Just to clarify, it's not that I don't want it to be dynamic, I just want the entire thing to not exist while that maze is being solved.
Also the only reason I wouldn't want to set it to nil is because I don't know how to restore its old physics body in an easy way.
The preferred way would be to physically remove your wall from your node tree. This will increase performance since the system does not have to check unnecessary nodes.
Just make sure that your wall is being retained when you remove it so that you can readd it when needed.
wall[location].removeFromParent()
...
scene.addChild(wall[location])
Sure
just set
isDynamic = false
wall.physicsBody?.collisionBitMask = 0
wall.physicsBody?.contactTestBitMask = 0
This pretty much isolates the SKPhysicsBody from the rest of the system. Do you have any SKJoints connected to your nodes with the bodies ? This can be awkward if you do.
or you can set it to nil
wall.physicsBody? = nil
then just make a method to set it again when your ready
func addPhysics() {
wall.physicsBody = SKPhysicsBody(circleOfRadius: 5)
wall.isDynamic = true
wall.physicsBody?.collisionBitMask = whatever
wall.physicsBody?.collisionBitMask = whatever
wall.physicsBody?.contactTestBitMask = whatever
}
If you do set to nil and then restore it make sure you do this at either the start or the end of the gameLoop.
In either of these override methods in the gameScene
override func update(_ currentTime: TimeInterval) {
}
override func didFinishUpdate() {
}
Hope this helps
You already have an answer, but another approach might be to subclass SKSpriteNode with one extra property called inactivePhysicsBody (or similar) which is an 'SKPhysicsBody'.
When you want to 'turn off' the physics for that node, set the inactivePhysicsBody property to physicsBody and then set physicsBody to nil. You can then easily restore the physics body when necessary.
Or you could add methods to the new class to do the same:
wall.activatePhysicsBody()
wall.inactPhysicsBody()
Give the object that owns the sprite an 'extra' reference to the sprite's physics body, e.g.:
init(sprite: SKSpriteNode) {
self.sprite = sprite
self.physicsBody = sprite.physicsBody
super.init()
}
Then you can set self.sprite.physicsBody to either self.physicsBody or nil to turn the object's physics on / off. Personally I'd avoid subclassing here but YMMV.

ARKit: How to place one imported 3D model above another?

I am working on a AR project using ARKit.
If I touch only the imported 3D object on a point, I want to place another 3D object above it.
(For example I have placed a table above which I have to place something else like a flower vase on the touched point).
How can I solve the problem that the second object should only be placed, when I touch the first 3D object?
The surface of the object is not flat, so I can not use hittest with bounding box.
One approach is to give the first imported 3D object a node name.
firstNode.name = “firstObject”
Inside you tapped gesture function you can do a hitTest like this
let tappedNode = self.sceneView.hitTest(location, options: [:])
let node = tappedNode[0].node
if node.name == “firstObject” {
let height = firstNode.boundingBox.max.y -firstNode.boundingBox.min.y
let position2ndNode = SCNVector3Make(firstNode.worldPosition.x, (firstNode.worldPosition.y + height), firstNode.worldPosition.z)
2ndNode.position = position2ndNode
sceneView.scene.rootNode.addChildNode(2ndNode)
} else {
return
}
This way when you tap anywhere else the 2nd object won’t get placed. It will only place when you tap on the node itself. It doesn’t matter where you tap on the node, because we only want the height & we can determine that from its boundingBox max - min which we then add to the firstnode.worldPosition.y
Make sure you set at the top of ARSCNView class
var firstNode = SCNNode!
this way we can access the firstNode in the tap gesture function.
Edit: If the first 3D model has many nodes. You can flattenNode on the parent Node in the sceneGraph (best illustrated with photo below). This removes all the childNodes and wraps from the sceneGraph. You can then just work with the parentNode.

Unexpected physicsBody in SpriteKit scene

I'm implementing a mass-spring system (many small physics bodies joined together with SKPhysicsJointSpring instances) with SpriteKit. Some of the particles would get snagged while traversing the center of the scene.
There seems to be a small, static body in the middle of the scene and I don't know why it's there.
Here's an easy way to see what I'm talking about:
In XCode 8, create a brand new project with the "Game" template.
In GameViewController.viewDidLoad(), add view.showsPhysics = true
If you run the project, you should see a little dot in the middle, which is the errant body:
Anyone know how to get rid of it?
Edit: I tried to manually create the scene object:
In GameViewController.viewDidLoad(), I replaced this:
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
view.presentScene(scene)
}
with this:
let scene = GameScene(size: view.frame.size)
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
view.presentScene(scene)
but that didn't fix it.
Anyways, I decided to make an answer because comments are not suitable due to lot of info I want to share. Also my answer, sadly, doesn't answer the question but it gives some useful info about this unidentified, obviously capable of flying (physics body) object :)
So this is the code how to grab it (and modify it???):
//you can use self.frame here...I just copied Alessandro's code
self.physicsWorld.enumerateBodies(in:(label?.frame)!) { body, stop in
if let node = body.node {
print("Type of this node: \(type(of:node))")
print("Frame of this node: \(node.frame))")
}else{
print("This body's node property is nil")
body.affectedByGravity = true
body.isDynamic = true
body.applyImpulse(CGVector(dx: 0.003, dy: 0.0003))
}
print("Area covered by this node physicsBody: \(body.area)")
}
So if you put a break point inside of that else statement, you can scan this physics body completely and get all the info about it, like that its node property is set to nil or that its isDynamic property is set to false. But you can change that, and like in my code, set for example isDynamics to true. This makes it moveable. So if you apply some forces to it, it will move.
Still, like I said in comments, I don't have an idea why it is there and what it represents or what is its purpose.
Also, for those who are wondering how it is possible that one physics body doesn't have a node associated with it ( body.node equals nil) but is still visible on screen when showsPhysics is set to true, there is a reasonable explanation. Physics world is separated from the node tree. So we can remove a sprite from a node tree, but that doesn't mean that its physics body will be removed instantly. It may happen that physics engine haven't finished simulation... So you probably wonder, how this might happen?
Let say you have three SKSpriteNode objects intersecting at the same time (say A contacts B and A contacts C at the same time). SpriteKit can process only one contact at time. And say that you are removing A from a parent when it is contacting with B. Then, there is a contact between A and C also, so didBegin:(_ contact) will be called twice. And if you remove A from its parent in first didBegin(_ contact) call, in the next didBegin(_ contact) call, bodyA.node will be nil (bodyA is a physics body of sprite A), but its physics body will remain visible until engine finishes what needed. This is because node tree and a physics world are separated.
About the "hello world" xCode game template , it seems a little physicsBody associated to the GameScene node.
With some code I've found this:
class GameScene: SKScene {
private var label : SKLabelNode?
private var spinnyNode : SKShapeNode?
override func didMove(to view: SKView) {
...
// End part of this function:
if let b = physicsWorld.body(in: (label?.frame)!) {
if let node = b.node {
print("Type of this node: \(type(of:node))")
print("Frame of this node: \(node.frame))")
}
print("Area covered by this node physicsBody: \(b.area)")
}
}
}
With a breakpoint to the last brace, you can see two bodies (maybe an array of bodies), one of them is the physicsBody to the left debugArea (array with index 1) with the same hex address as my body b in my code : 0x79794f90, a little rectangle body with area 4.444
Printing description of ((PKPhysicsBody *)0x79794f90):
<SKPhysicsBody> type:<Rectangle> representedObject:[<SKScene> name:'(null)' frame:{{-375, -667}, {750, 1334}} anchor:{0.5, 0.5}]
(lldb)
I had a similar problem. I have a game with two sprite nodes joined together (SKPhysicsJointFixed.joint) moving around an SKEditor created scene.
As per my design, this node-pair would impact a third sprite node and be propelled smoothly away from the third sprite node, EXCEPT when the impact was in the center of the scene. For the center of the scene impact, the node-pair would compress together while be propelled away from the third sprite node, presenting a poor graphical image.
After significant time debugging my code, I found this post. Kudos for the explanations and code. I can’t answer the “why” question but for the “particles would get snagged while traversing the center of the scene” question my suggested solution is to clear the collisionBitMask instead of moving the body.
BTW categoryBitMask is 0 when loaded.
//moves the problem offscreen unless it hits another node
//body.affectedByGravity = true
//body.isDynamic = true
//body.applyImpulse(CGVector(dx: 0.003, dy: 0.0003))
//
// collisionBitMask loads with 4294967295 = default value to collide with all categories
//
body.collisionBitMask = 0

Strange SpriteKit For Loop Bug

I am encountering a very strange bug that I just can't figure out. In my game, you are driving a car on a 2D plane trying to avoid obstacles (like a rock or car). The obstacles can appear on 3 different paths. Sometimes there is just one obstacle on any of the paths or sometimes there is two obstacles on two of the three paths.
The problem I am experiencing is that when two obstacles appear and then get deleted when they go off the screen, one of the obstacles currently moving on the screen is also "deleted" (I know this because the console prints "deleted object" 3 times). But, the obstacle that is randomly "deleted" is not removed from view. Instead, it freezes where it previously was and never goes away.
Here is the code block I am having problems with:
var canGetScore = true
for (num,obj) in enumerate(obstaclesToMove) {
obj.position.y -= CGFloat(gameSpeed)
if obj.position.y <= userCar.position.y && obj.name == "NotPassed" {
obj.name = "Passed"
if canGetScore {
canGetScore = false
score += 1
scoreLabel.text = "\(score)"
}
}
if obj.position.y <= -CGRectGetMidY(self.frame) {
obj.removeFromParent()
obstaclesToMove.removeAtIndex(num)
println("deleted object")
}
}
Variable Key:
"obstaclesToMove" is an array of obstacles(SKSpriteNodes) that should be moved down the screen.
"gameSpeed" is an integer of how many pixels the screen moves every frame (the value is 5 in this instance).
"userCar" is the character that the user controls (SKSpriteNode).
The "canGetScore" thing is to eliminate another bug I was experiencing before. I know for a fact this is not causing the current bug I am having.
Does anyone have any ideas to why this is happening? If you need any more explanation just ask.
EXTRA NOTE: The user's car technically never moves, but the background and the obstacles move.
I had the same thing to implement in my game which is very similar to yours, so I guess that everything I've done can be applied to your situation. What I did instead of using indexes to remove obstacles from array of obstacles is that I've used addObject and removeObjectIdenticalTo methods(self.obstacles is NSMutableArray):
[self.obstacles addObject:asteroide]; //asteroide is subclass of SKSpriteNode
[self.obstacles addObject:enemyShip];//enemyShip is subclass of SKSpriteNode
And when I removing them, I just do something like:
[self.obstacles removeObjectIdenticalTo:asteroide];
[self.obstacles removeObjectIdenticalTo:enemyShip];
This worked for me... One more thing I would like to point is how you can remove obstacles when offscreen in a different way than yours. This is how I do that:
SKAction *sequence = [SKAction sequence:#[moveAsteroide,[SKAction runBlock:^{[asteroide removeFromParent]; [self.obstacles removeObjectIdenticalTo:asteroide];
}]]];
[asteroide runAction:sequence withKey:#"moving"];
So I run the sequence which first move object to desired location, and that is some point which is offscreen (y = -obstacle.size.height) and after that, I remove it from parent and update obstacles array. This works for me because I use actions to move obstacles, but maybe it can work for you if you are moving obstacles in same way. I like this more than constantly checking the location of obstacle in update method.
This is just my way though, and all this can be done in many ways, and you have to choose which one suits you better. Hope this make sense and helps a bit :-)
It seems to me that the problem is in how you remove your object from obstaclesToMove
Imagine you have three objects in your array:
obstaclesToMove = [object1, object2, object3]
You have to remove object 1 & 2 because they have to go off screen, so what you do in your code is first remove object 1 then object 2, except in your code it translates to :
obstaclesToMove.removeAtIndex(0)
obstaclesToMove.removeAtIndex(1)
You would think this isn't a problem, except look what happens to your array while you do that:
obstaclesToMove = [object1, object2, object3]
obstaclesToMove.removeAtIndex(0)
// obstaclesToMove = [object2, object3]
obstaclesToMove.removeAtIndex(1)
// obstaclesToMove = [object2]
Usually it is a bad idea to remove object from an array that you are looping through. What you could do to make it safer is:
Collect all the index you need to remove in an array
Sort them in reverse order
Delete the items from the array
You could do it like that:
var indexToDelete: [MyObstacleObject] = []
var canGetScore = true
for (num,obj) in enumerate(obstaclesToMove) {
obj.position.y -= CGFloat(gameSpeed)
if obj.position.y <= userCar.position.y && obj.name == "NotPassed" {
obj.name = "Passed"
if canGetScore {
canGetScore = false
score += 1
scoreLabel.text = "\(score)"
}
}
if obj.position.y <= -CGRectGetMidY(self.frame) {
indexToDelete.append(num)
}
}
indexToDelete.sort({ $0 > $1 })
for item in indexToDelete {
obstaclesToMove[item].removeFromParent()
obstaclesToMove.removeAtIndex(item)
println("deleted object")
}
That way when you delete items from your array you can be sure that they will still have the right index since you will be first deleting items with a higher index.

How to know whether an SKSpriteNode is not in motion or stopped?

In my game, I need to do some setting changes after my SKSpriteNode stops moving.
First,
I apply force on my SKSpriteNode, so it starts moving due to the force acting on it.
Now, i need to know when it stops moving.
I know that SKSpriteNode has a speed property, but it is always set to 1.0 by default.
Any ideas?
You can check a node's velocity by using something like this:
if((self.physicsBody.velocity.dx == 0.0) && (self.physicsBody.velocity.dy == 0.0)) {
NSLog(#"node has stopped moving");
}
The usual place to put the above code would be in the update method.
Since you are using physics you can use the resting property of SKPhysicsBody.
if sprite.physicsBody?.resting == true {
println("The sprite is resting")
} else {
println("The sprite is moving")
}
Hope this helps.
You can subclass SKSpriteNode and add a previousPosition property on it, which you can update on -didFinishUpdateMethod. If there is no change in the position you can assume the node is stopped (you might have to get a median of the last few positions to smooth it out). The speed property is something different, according to the class reference, speed is:
A speed modifier applied to all actions executed by a node and its descendants.
Hope this helps!
Danny

Resources