I am evaluating the iOS SpriteKit physics engine, and for a test I have created a simple scene with Xcode that contains two circle-shaped nodes:
Both nodes have physics bodies of circular shape and 5 kg of mass.
I am connecting both nodes with a SKPhysicsJointSpring. Here is the entire setup (in viewDidLoad()):
let path = NSBundle.mainBundle().pathForResource("MyScene", ofType: "sks")!
let scene = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as! SKScene
let border = SKPhysicsBody(edgeLoopFromRect: scene.frame)
border.restitution = 0;
border.friction = 0;
scene.physicsBody = border
let player = scene.childNodeWithName("player")! // large ball
let tail1 = scene.childNodeWithName("tail1")! // smaller ball
player.physicsBody!.usesPreciseCollisionDetection = true
tail1.physicsBody!.usesPreciseCollisionDetection = true
let spring1 = SKPhysicsJointSpring.jointWithBodyA(player.physicsBody!, bodyB: tail1.physicsBody!, anchorA: player.position, anchorB: tail1.position)
spring1.damping = 1
spring1.frequency = 3
scene.physicsWorld.addJoint(spring1)
spriteKitView.presentScene(scene)
Note: The gravity is set to (0,0) in the scene editor.
When I move the bigger node (say, by capturing touches with touchesBegan() and setting the node's position to the touch position), the other follows according to spring parameters.
However, if I move the node fast enough, so that the spring forces become extreme, both nodes overlap when the spring is contracting. I am expecting them to collide with each other since their collisionBitMask is by default set to -1 (all bits set). I have enabled usesPreciseCollisionDetection but the effect is still visible.
When I add an edge loop around the scene they do collide with that edge as expected. Same with additional nodes that have a physics body (but no joints attached).
I have the impression that the presence of the spring somehow makes the engine ignore collisions between nodes that are connected with joints.
Did anyone else observe this, too?
Did I forget anything? Or is this working as intended?
Attempting to answer my own question.
I found out that other joints (such as SKPhysicsJointLimit) disable collision between the joined nodes as well. My only conclusion is that all SpriteKit joints somehow consider two objects as one, where collisions are deemed undesirable. Though in my app I would prefer to have collisions enabled. Maybe I should file an enhancement at Apple developer feedback.
So far my workarounds include:
Create spring behaviour by adding a SKFieldNode.springField plus a SKFieldNode.dragField as children to the sprite node. Pro: as children these fields automatically follow their parent position; Con: this spends 2 precious bits of fieldBitMask which puts a natural limit to the number of such nodes.
Manually apply some spring + drag force at every frame. Pro: does not waste bits of fieldBitMask, Con: compute spring + drag forces manually.
So I tend to favor option 2.
To summarize: If you want spring behaviour between 2 nodes while maintaining collisions, implement forces manually and do not use a joint.
Related
My goal is to create a single physics body out of several of SceneKit's SCNBox geometries.
My understanding is that when I pass an SCNPhysicsShape(shapes:transforms:) to an SCNPhysicsBody(type:shape:), it should create a single physics body.
However, I end up with something that seems suspiciously like it's not a single physics body at all, but rather several separate physics bodies -- one for each shape I passed into SCNPhysicsShape(shapes:transforms:).
When I turn on scnView.debugOptions = .showPhysicsShapes, I can clearly see red lines defining the separate bodies in question. On its own, this isn't very convincing evidence (it's conceivable that those lines could be shown for whatever reason while still being a single physics body).
But there's another piece of data, here: The project in which I'm encountering this issue features a small ball that rolls around the scene -- and when that ball rolls over the red lines in question, the ball bounces up into the air a bit. So, it's quite obvious that, whatever is actually going on, there are edges where I would expect to see none.
This behavior is clearly visible in the following GIF. In it, each colored block is a separate SCNBox geometry with its own physics body. Each block has the exact same position.z. The ball bounces considerably as it crosses the point where one geometry meets another.
Here's some code illustrating the issue. parent is an SCNNode that holds the child nodes, and is the node to which I assign the physics body. Please assume that all properties are defined; I'm omitting things that aren't terribly relevant.
let childShape1 = SCNBox(width: 6, height: 2, length: 6, chamferRadius: 0.0)
//Other child shapes defined here...
//Set up the positional translation relative to the child node's parent:
let translateMatrixShape1 = SCNMatrix4MakeTranslation(childShape1.position.x, childShape1.position.y, childShape1.position.z)
//Other child translations defined here...
let parentShape = SCNPhysicsShape(shapes: [childShape1, childShape2, childShape3, childShape4], transforms: [translateMatrixShape1, translateMatrixShape2, translateMatrixShape3, translateMatrixShape4])
parent.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.static, shape: parentShape)
Now, parentShape is four rectangular boxes arranged around a central point, creating a sort of picture-frame-shaped object.
The ball is an SCNNode with an SCNSphere geometry and a dynamic physics body.
Question: Does anyone have any idea what might be going on, here? Have I somehow misunderstood how this whole thing works, or is this a limitation of SceneKit?
Usually you can create one single PhysicsBody from several geometry objects by making a flattenendClone from out of your (i.Ex.) parent node. This new node will have one single geometry then. For the PhysicsBody you can then use the geometry element of the new node. In Addition I recoommend to use a concavePolyhedron for the static body type. (I hope I understood you correctly)
I'm trying to create an application in which the user stacks different geometric shapes. In a .scn file, which is loaded inside the ARSCNView, I insert a static plane and then at each tap of the user, the app inserts a dynamic SCNNode.
The first node is being inserted a few inches above the plane, to replicate a falling object. And then, each other node is being dropped on top of another.
This is the main idea of the application; the problem appears after adding 3 or 4 nodes, they appear to slide of each other, almost jiggle, and the whole structure collapses.
This is my node I'm inserting:
let dimension: CGFloat = 0.075
let cube = SCNBox(width: dimension, height: dimension, length: dimension, chamferRadius: 0.0)
let node = SCNNode(geometry: cube)
node.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.dynamic, shape: nil)
node.physicsBody?.mass = 2.0
node.physicsBody?.friction = 1.0
node.physicsBody?.restitution = 0.01
node.physicsBody?.damping = 0.0
node.physicsBody?.angularDamping = 0.0
node.physicsBody?.rollingFriction = 1.0
node.physicsBody?.allowsResting = true
let insertionYOffset = 0.3
node.position = SCNVector3(hitResult.worldCoordinates.x, hitResult.worldCoordinates.y + Float(insertionYOffset), hitResult.worldCoordinates.z)
I've tried to play with the values and these are the best ones, but they aren't enough to create a stable structure of blocks.
As a requirement, I need to keep the blocks dynamic, they need to be affected by gravity, wind, etc.
The problem most likely has something to do with a factor called Dynamic which will allow it to continuously move or may have to do with the objects hitting each other to fix that problem all you have to do is change the collision mask to two different numbers.
I see two points that may be the flaws of your simulation:
Your objects are stacked. When two objects collides there's a small amount of error added in order to keep the physics in a stable phase and prevent weird behaviors. So when you stack objects you add error amounts and the simulation becomes inaccurate. I suggest making objects static over time or depending of the position, or reduce gravity by a bit.
Most of the physics engines works better with objects sized with the unit size (most of the cases, it's 1 like in Box2D or PhysX). Objects that are very small or very big tend to act less natural. 0.075 sounds a bit small for a simulation. I don't see a obvious solution, maybe look for a way to change the reference size.
I am seeming to have difficulty keeping my sprite nodes inside a map boundary that I have set up in the following way:
I have an SKNode *enemy that moves around an SKScene by goals and behaviors courtesy of GameplayKit from iOS 9. Currently the node wanders, and avoids obstacles that are defined GKPolygonObstacle objects. I have my bitmask set up so that any obstacle is deemd category wall, which the node is told to collide with (AKA disallow passing through).
In my didBeginContact:(SKPhysicsContact *)contact I am handling these collisions. All is working exactly as planned and there are no issues when manually moving this enemy.
However, the problem begins when I have the enemy wander around the scene through an SKGoal *wanderGoal set up as
// Low --> 0.5 Lowest --> 0.25
enemy.wanderGoal = [GKGoal goalToWander:low];
[enemy.agent.behavior setWeight:lowest forGoal:enemy.wanderGoal];
// Add obstacles to avoid for each of the inner map nodes
// that act as impassible areas for the enemy to pass through.
NSArray *obstacles = [SKNode obstaclesFromNodePhysicsBodies:innerMapArray];
/** This goal does not change --> enemies will ALWAYS
avoid the obstacles in the level map. This should always
be set to the highest priority of the enemy goals
*/
enemy.avoidGoal = [GKGoal goalToAvoidObstacles:obstacles maxPredictionTime:10];
// Highest --> 250
[enemy.agent.behavior setWeight:highest forGoal:enemy.avoidGoal];
The enemy moves randomly as expected, and seems to avoid the obstacles most of the time...yet on the occasion the enemy does in fact pass through the map barrier. Let me expand on this:
The map barrier is essentially a rectangle, and I have set up 2 nodes that act as a shell that represent the left half and the right half of this shape. These nodes have a thickness of 100 points so they are not simply CGPath refs but rather shapes. They act as these two brackets act: [] (my halves are flush touching at the middle).
My question is such:
How can I prevent the enemy from passing through these obstacles with using either physics with the bitmask categories, or by the goal behavior? (Or both together)
I understand that while a GKGoal is a suggested behavior it does not guarantee this goal to be reached (but it can be ~95% sure it wont happen as Apple's AgentsCatalog shows). But, what I don't understand is why the enemy does not simply "slide" along the edge of the boundary as the goal tells it to (try to) "pass" through such boundary.
This "sliding" behavior does happen when I manually move the enemy around the scene, and as I attempt to move it passed a boundary deemed an obstacle, it slides towards the direction that I am having it move (just as any video game does that has map boundaries).
SO
If someone can help me prevent this from happening / explain how to approach preventing the enemy from ever passing through these (supposedly) "impassible" areas, I would be most appreciative.
FYI: If you are unclear with what I am describing / how I have created any objects, please ask and I will provide more info to help.
I'm looking for the best way (performace-wise) to detect contact between two objects that do not collide (not bounce off each other) in a SceneKit physics world.
I saw that SpriteKit has a contactTestBitMask and a collisionBitMask for physics bodies while SceneKit only has the latter. So there must to be another preferred way to get notified when objects have contact in SceneKit. I guess that calling contactTestBetweenBody:andBody:options: in each frame for each object is not the best way to do it?
UPDATE
With iOS 9.0, Apple has added contactTestBitMask to SCNPhysicsBody. So this question will become obsolete soon.
This question deserves a full tutorial. In short, each physics body has a categoryBitMask and collisionBitMask.
categoryBitMask
This is the bit that represents the body.
collisionBitMask
This is the bit that represents which bodies collide with it.
By default (when assigned a physicsBody) all bodies collide with the exception of a kinematicBody, which act special. Objects don't collide with it. But don't get confused, it can collide with other objects when manually moved (such as by dragging it with a finger).
How to handle!!!
Assign your bodies whether it be static or dynamic. Assign there category and collision mask. If the mask match up they collide. How about an example!
These bodies collide with each other
bodyA.physicsBody.categoryBitMask = 4;
bodyB.physicsBody.categoryBitMask = 8;
bodyA.physicsBody.collisionBitMask = 8;
bodyB.physicsBody.collisionBitMask = 4;
These don't collide
bodyA.physicsBody.categoryBitMask = 4;
bodyB.physicsBody.categoryBitMask = 8;
bodyA.physicsBody.collisionBitMask = 0;
bodyB.physicsBody.collisionBitMask = 0;
Noticed I used categories by power of two. This is because the contactDelegate uses bitwise AND to compare. Read up on bitwise if you don't understand. It will really help.
These bodies collide without physics
EXAMPLE: You have a hero that runs through a ghost and you want to know that it happened without either one being effected. Such as bouncing off each other.
bodyA.physicsBody.categoryBitMask = 4;
bodyB.physicsBody.categoryBitMask = 8;
bodyA.physicsBody.collisionBitMask = 0;
bodyB.physicsBody.collisionBitMask = 0;
So the following code is designed so that bodyA(your hero) runs through bodyB(ghost).
bodyB.physicsField.categoryBitMask = 4
Noticed that the above is a physicsField.catagoryBitMask. It is assigned a 4 so that it matches up with bodyA.
let collisionField = SCNPhysicsField.customFieldWithEvaluationBlock(){
(_, _, _, _, _) in
WHAT U WANT TO DO ON CONTACT
}
bodyB.physicsField = collisionField
The physicsField is not the shape of your geometry unfortunately. Its default is the shape of its bounding box or sphere/elliptical if you set the physicsField property usesEllipsoidalExtent to "true/yes"
The area of effect can be changed using the physicsFields halfExtent property like so...
bodyB.physicsField.halfExtent = SCNVector3Make(.5, .5, .5)
The above will extend the field of a box the size of (2, 2, 2) to (2.5, 2.5, 2.5).
EDIT: If they still collide, try removing the physicsbody from bodyB (this will remove all physics simulation from the node). I don't think it will be a problem as long as the catagory and collision bitmask are assigned properly.
This is the best method I could find. All coding is from my head, with the support of Apple docs, so may not be exactly right but I hope this helps!
I have found the best way is to just set the mass so small on a kinematic body that the other one will pass through it.
sensorBox.physicsBody.mass = 0.00000000001; // super tiny mass makes it a "sensor"
Don't forget your scene's physics world can call a "contactTestBetweenBody: andBody: withOptions... this works great.
Usage
[myScene.physicsWorld contactTestBetweenBody:(SCNPhysicsBody *) andBody:(SCNPhysicsBody *) options:(NSDictionary *)];
I'm working on a piece of code, and I want there to be gravity in the view but I want specific projectiles to defy gravity and fly across the screen. I know to get rid of the gravity on the whole view its just:
self.physicsWorld.gravity = CGVectorMake(0, 0);
But as stated I want gravity on the scene.
So I'm wondering if there is a way to take the gravity off one specific item? (i.e. the SKSpriteNode _debris item in my case)
By setting the physicsBody to not be affected by gravity.
E.g.
myNoGravityObject.physicsBody.affectedByGravity = NO;
See the documentation of SKPhysicsBody.
If you don't want a node to interact with physics calculations at all, but still want it to have a physicsBody (i.e. to make it begin to interact with things later on, or to check for collisions with other nodes), you can set
node.physicsBody.dynamic = NO;
This will cause the node to ignore gravity, as well as collisions, impulses, and the like. If you are setting up the contact delegate, note that at least one node in any given contact must be dynamic for the contact delegate to be notified of the event.