FREELessons: 13Length: 1.2 hours

Next lesson playing in 5 seconds

  • Overview
  • Transcript

2.4 Collision Detection and Applying Force

In this lesson you'll learn how to make objects respond to collisions with the surface and each other. I'll also show you how to apply custom forces to your physics bodies.

2.4 Collision Detection and Applying Force

Hi, and welcome back to Get Started With Augmented Reality for iOS. In this lesson you are going to learn about how easy it is to use collision detection and apply forces to your physics models. We've already talked a little bit about collision detection in the last lesson. Now, I want to elaborate on that. Even though ARKit and SceneKit are high level frameworks, it is good to understand their theoretical models they are based on. Collision Detection is about determining whether two objects have collided, meaning they intersect or touch each other's physics bodies. Collision detection can either be done a priori or a posteriori, meaning before or after the collision occurs. I'm not 100% certain, but pretty sure Apple uses the latter for its physics simulation. An a priori collision detection tries to extrapolate movement of path objects to determine if and when collisions occur before they do. It is more precise because it doesn't need to fix anything but more difficult to do. A posteriori detection does simple intersection testing after tiny time steps to see if two objects intersect. Depending on the speed of the object, and on the time gap between tests, there can be cases where objects miss each other, or are further intersected, that can be ignored. So, corrections have to be done to resolve this collision. If you have n objects in a scene that are physics enabled, you have to do n squared tests to see if a collision occurred. The more complex an object is, the more expensive each test is. One very easy optimization that can be done is using bounding boxes. It is very easy and fast to determine if two cubes intersect. A bounding box wraps around the model so that the whole geometry is contained within it. If two bounding boxes intersect, the pair is selected for more detailed testing. There are, of course, more advanced techniques that are used in other areas of computer graphics as well, but I'm not going to get into them in this lesson. To implement Collision Detection in SceneKit, you have to use the categoryBitMask property on the physics body. You can define up to 32 categories and set multiple parts, so an object can belong to different categories it wants. To specify with which other bodies an object should collide, you use the collisionBitMask. It uses the logical AND operator to compare it to another body's categoryBitMask to see if it is a serial value. If it isn't, then collisions can occur. Here is an example. For instance, we have a body that has the categoryBitMask of 10, or the decimal number 2. And another body that has 01, or decimal 1. For the collisionBitMask, those bodies won't interact with each other. If the first body in this example would have a 11, or .3 mask, then they would, because the last bit will survive a Logical AND operation. By default, static bodies have a BitMask of 10, and dynamic and kinematic bodies, a Mask of 1. All collisionBitMasks are set to all 1s by default. So let's take the project we used for plane detection, and use it to add some boxes that are physics-enabled, and can interact with the planes and each other. As you already know, switching on collision detection is very simple. Basically you just have to add a body to every physics enabled object. I intentionally ignored the plane last lesson, as adding a static PhysicsBody would already enable collisions. So, let's add a PhysicsBody of type static here. I'm also going to help sync it by providing a geometry inform of an SNCPhysicsShape. It's the exact get geometry of the plane as it is a very simple object. The only difference here is that we have to update the shape whenever the plane changes. As ARKit detects new parts of it and changes the anchor, we can just reinitialize the whole body. Let's try it out. I can now drop some cubes on the table that are interacting with each other as well as the plane. Back in the project, I'm going to add an enum called Physics category of type int. Another case for box with a binary representation of 01 as well as a plane that is binary 10. We can now set the category BitMask of the plane to plane. And also change the key ops collisions BitMask to just the plane. This means that the cubes shouldn't interact with each other anymore. Let's run the app again and try it out. When I add some boxes, they intersect with each other, but interact with the plane. Okay, enough about collision detection. Let's talk about force. A force is a vector that affects dynamic bodies. For instance, by dragging them down constantly which is what gravity does. Gravity in a physics simulation is nothing else than a constant downward force. The second type of force that can be applied is an impulse. This is a one time force that gives an object an initial velocity, but isn't permanent. Both types are modeled with real world systems in mind. A force is typically applied in Newtons, which is kilogram meters per second squared or mass times acceleration. An impulse is given in Newton seconds, or kilogram meters per second, which is the international unit for an impulse. If you bring mass into the mix, your physics bodies are required to have it. By default, a dynamic body has a mass of one kilogram. In fact, physics goes much further than that in SceneKit. Bodies have electric charge, surface friction, restitution and tampering properties. They are used for various physics based simulation centers. You can apply forces to physicsBody.applyForce. And used here is impulse argument to define it as a force or an impulse. Some forces are applied to all dynamic elements automatically not gravity. These are implemented as SCNPhysicsField. Such fields can have a drag that slows objects down, vortexes that are circular forces, gravity, either radial or linear. And there are even turbulence fields that apply random forces within an area. Because I mentioned electricity earlier, I also have to state that there can be electric and magnetic fields that affect objects with electric properties. As you can see there's a lot of different ways you can simulate things with physics and SceneKit, and create a truly complex augmented reality experience. In our case, we are going to keep it simple though and just use an impulse and a physics field in our example. My goal now is instead of putting the box somewhere on the plane, to shoot it from my viewpoint in front of me. Be aware there is some math incoming. Don't say I didn't warn you. Okay, so let's start at the end. We want to apply a force in a certain direction as an input. To calculate the direction, we need to determine the camera rotation. We can get all of this information from the transformation matrix. To get that, we can ask the AR session of the sceneView for the current frame and its camera. If we have a camera because it's an optional we can use its transform matrix, otherwise we use the identity matrix. We could extract the position from the transformation matrix as well by using the fourth column. But doing it this way, the cubes are oriented the same way the camera is. To get the directional, we have to first extract the rotation matrix. It is a 3x3 matrix within the transformation matrix starting from the top left. We can multiply this with a camera viewing direction, which is 0, 0, -1, and get the direction vector. Since I want it to be a little bit stronger, I'm using -2 instead. Finally, I have to convert it to an SCNVector3, so we can use it with SceneKit. Okay, let's try it out. I'm now throwing cubes from my vantage point onto the plate. For the final part of the lesson, I want to add a physics field to the scene. First of all, let's undo the collisionBitMask, so everything interacts with everything again. Since it doesn't move, let's do it right at the beginning. The first thing to do is to create a new SCN note. Then, we can attach a physics field to it. The different types are implemented as static methods under the SCNPhysicsField clause. I'm going with noiseField, as it introduce random forces that are not depending on the object's velocity. Smoothness describes the amount of randomness, and animation speed changes over time. If you want no noise, you would use 1.0 for smoothness. If you want the static field, you would use 0 for animation speed. The extend determines the size of the field. The vector marks one end of the box and the negative vector the opposite end. Strength is a generic property for defining how much the field influences objects. This is different for every type of field. Okay, finally we need a position. And I'm using the zero point which is the spot the user is standing in when launching the app. So let's try it out. On this end of the table, everything moves. But when I walk to the other side of it, it's completely quiet. Because the physics field is only three meters in each direction of our initial point. Let's try to shoot a cube from this end where it flies without noise, to the area where noise affects the cube. You can see the border where the field start having an affect on the cubes. To recap, collision detection works by using different intersection or prediction techniques. A collision in SceneKit will be triggered if the collisionBitMask contains the category bits that are set with the categoryBitMask and the other object. Everything in SceneKit related to physics, uses real world counterparts. Forces and physics fields are applied to a node or a seed and can affect the movement. In our next lesson we are going to talk about lighting in ARKit. See you there.

Back to the top