I have been creating my own very simple test game based on Breakout while learning SpriteKit (using iOS Games by Tutorials by Ray Wenderlich et al.) to see if I can apply co
The problem is twofold in that 1) it will not be solved by altering friction/restitution of the physics bodies and 2) will not be reliably addressed by a return impulse in the renderer() loop due to the contact occurring after the body has already begun decelerating.
Issue 1: Adjusting physics properties has no effect -- Because the angular component of the collision is below some predetermined threshold, the physics engine will not register it as a physical collision and therefore, the bodies will not react per the physics properties you've set. In this case, restitution will not be considered, regardless of the setting.
Issue 2: Applying an impulse force when the collision is detected will not produce consistent results -- This is due to the fact that in order to simulate restitution, one needs the velocity of the object just prior to impact.
-->For instance, if an object hits the floor at -10m/s and you want to simulate 0.8 restitution, you would want that object to be propelled 8m/s in the oppostie direction.
Unfortunately, due to the render loop, the velocity registered when the collision occurs is much lower since the object has already decelerated.
-->For example, in the simulations I was running, a ball hitting a floor at a low angle was arriving at -9m/s, but the velocity registered when the collision was detected was -2m/s.
This is important since in order to create a consistent representation of restitution, we must know the pre-collision velocity in order to arrive at our desired post-collision velocity...you can't ascertain this in the Swift collision callback delegate.
Solution: Step 1. During the render cycle, record the velocity of the object.
//Prior to the extension define two variables:
var objectNode : SCNNode!
var objectVelocity : SCNVector3!
//Then, in the renderer delegate, capture the velocity of the object
extension GameViewController: SCNSceneRendererDelegate
{
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval)
{
if objectNode != nil {
//Capture the object's velocity here, which will be saved prior to the collision
if objectNode.physicsBody != nil {
objectVelocity = objectNode.physicsBody!.velocity
}
}
}
}
Step 2: Apply a return impulse when the object collides, using the velocity saved prior to the collision. In this example, I am only using the y-component since I am simulating restitution in that axis.
extension GameViewController: SCNPhysicsContactDelegate {
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
let contactNode: SCNNode!
//Bounceback factor is in essence restitution. It is negative signifying the direction of the vector will be opposite the impact
let bounceBackFactor : Float! = -0.8
//This is the slowest impact registered before the restitution will no longer take place
let minYVelocity : Float! = -2.5
// This is the smallest return force that can be applied (optional)
let minBounceBack : Float! = 2.5
if contact.nodeA.name == "YourMovingObjectName" && contact.nodeB.name == "Border" {
//Using the velocity saved during the render loop
let yVel = objectVelocity.y
let vel = contact.nodeA.physicsBody?.velocity
let bounceBack : Float! = yVel * bounceBackFactor
if yVel < minYVelocity
{
// Here, the opposite force is applied (in the y axis in this example)
contact.nodeA.physicsBody?.velocity = SCNVector3(x: vel!.x, y: bounceBack, z: vel!.z)
}
}
if contact.nodeB.name == "YourMovingObjectName" && contact.nodeA.name == "Border" {
//Using the velocity saved during the render loop
let yVel = objectVelocity.y
let vel = contact.nodeB.physicsBody?.velocity
let bounceBack : Float! = yVel * bounceBackFactor
if yVel < minYVelocity
{
// Here, the opposite force is applied (in the y axis in this example)
contact.nodeB.physicsBody?.velocity = SCNVector3(x: vel!.x, y: bounceBack, z: vel!.z)
}
}
}
}