3d Game Physics Tutorial Homework


Hi, I’m Glenn Fiedler and welcome to Game Physics.

In the previous article we discussed how to integrate our physics simulation forward at fixed delta time increments, regardless of display framerate.

In this article we are going to simulate motion in three dimensions.

Rigid Bodies

We will concentrate on a type of object called a rigid body. Rigid bodies cannot bend, compress or deform in any way. This makes their motion much easier to calculate.

To simulate the motion of rigid bodies, we must study both rigid body kinematics and rigid body dynamics. Kinematics is the study of how an object moves in the absence of forces, while dynamics describes how an object reacts to them. Together they provide all the information you need to simulate the motion of a rigid body in three dimensions.

Along the way I will show you how to integrate vector quantities, handle rotations in three dimensions and integrate to find the motion of your object as it moves and spins around the world.

Moving in the Third Dimension

As long as we only have single floating point values for position and velocity our physics simulation is limited to motion in a single dimension, and a point moving from side to side on the screen is pretty boring!

We want our object to be able to move in three dimensions: left and right, forward and back, up and down. If we apply the equations of motion to each dimension separately, we can integrate each dimension in turn to find the motion of the object in three dimensions.

Or… we could just use vectors.

Vectors are a mathematical type representing an array of numbers. A three dimensional vector has three components x, y and z. Each component corresponds to a dimension. In this article x is left and right, y is up and down, and z is forward and back.

In C++ we implement vectors using a struct as follows:

struct Vector { float x,y,z; };

Addition of two vectors is defined as adding each component together. Multiplying a vector by a floating point number is the same as just multiplying each component. Lets add overloaded operators to the vector struct so that we can perform these operations in code as if vectors are a native type:

struct Vector { float x,y,z; Vector operator + ( const Vector &other ) { Vector result; result.x = x + other.x; result.y = y + other.y; result.z = z + other.z; return result; } Vector operator*( float scalar ) { Vector result; result.x = x * scalar; result.y = y * scalar; result.z = z * scalar; return result; } };

Now instead of maintaining completely seperate equations of motion and integrating seperately for x, y and z, we convert our position, velocity, acceleration and force to vector quantities, then integrate the vectors directly using the equations of motion from the first article:

F = ma dv/dt = a dx/dt = v

Notice how F, a, v and x are written in bold. This is the convention used to distinguish vector quantities from single value (scalar) quantities such as mass m and time t.

Now that we have the equations of motion in vector form, how do we integrate them? The answer is exactly the same as we integrated single values. This is because we have already added overloaded operators for adding two vectors together, and multiplying a vector by a scalar, and this is all we need to be able to drop in vectors in place of floats and have everything just work.

For example, here is a simple Euler integration for vector position from velocity:

position = position + velocity * dt;

Notice how the overloaded operators make it look exactly the same as an Euler integration for a single value. But what is it really doing? Lets take a look at how we would implement vector integration without the overloaded operators:

position.x = position.x + velocity.x * dt; position.y = position.y + velocity.y * dt; position.z = position.z + velocity.z * dt;

As you can see, its exactly the same as if we integrated each component of the vector separately! This is the cool thing about vectors. Whether we integrate vectors directly, or integrate each component separately, we are doing exactly the same thing.

Structuring for RK4

In the example programs from previous articles we drove the simulation from acceleration assuming unit mass. This kept the code nice and simple, but from now on every object will have its own mass in kilograms so the simulation needs be driven by forces instead.

There are two ways we can do this. First, we can divide force by mass to get acceleration, then integrate this acceleration to get the velocity, and integrate velocity to get position.

The second way is to integrate force directly to get momentum, then convert this momentum to velocity by dividing it by mass, then finally integrate velocity to get position. Remember that momentum is just velocity multiplied by mass:

dp/dt = Fv = p/m dx/dt = v

Both methods work, but the second way is more consistent with the way that we must approach rotation later in the article, so we’ll use that.

When we switch to momentum we need to make sure that the velocity is recalculated after each integration by dividing momentum by mass. Doing this manually everywhere that momentum is changed would be error prone, so we now separate all our state quantities into primary, secondary and constant values, and add a method called ‘recalculate’ to the State struct which is responsible for updating all the secondary values from the primary ones:

If we make sure that recalculate is called whenever any of the primary values change, then our secondary values will always stay in sync. This may seem like overkill just to handle converting momentum to velocity, but as our simulation becomes more complex we will have many more secondary values, so it is important to design a system that handles this.

Spinning Around

So far we have covered linear motion, we can simulate an rigid body so that it moves in 3D space, but it cannot rotate yet.

The good news is that rotational equivalents to force, momentum, velocity, position and mass exist, and once we understand how they work, integration of rotational physics state can be performed using our RK4 integrator.

Let’s start off by talking about how rigid bodies rotate. Because our objects are rigid they cannot deform. This means that we can treat the linear and rotational parts of an object’s motion as being entirely separate: a linear component (position, velocity, momentum, mass) and a rotational component rotating about the center of mass.

How do we represent how the object is rotating? If you think about it a bit, you’ll realize that for a rigid body rotation can only ever be around a single axis, so the first thing we need to know is what that axis is. We can represent this axis with a unit length vector. Next we need to know how fast the object is rotating about this axis in radians per second.

If we know the center of mass of the object, the axis of rotation, and the speed of rotation then we have the all the information we need to describe how it is rotating.

The standard way of representing rotation over time is by combining the axis and the speed of rotation into a single vector called angular velocity. The length of the angular velocity vector is the speed of rotation in radians while the direction of the vector indicates the axis of rotation. For example, an angular velocity of (2Pi,0,0) indicates a rotation about the x axis doing one revolution per second.

But what direction is this rotation in? In the example source code I use a right handed coordinate system which is standard when using OpenGL. To find the direction of rotation just take your right hand and point your thumb down the axis, your fingers curl in the direction of rotation. If your 3D engine uses a left handed coordinate system then just use your left hand instead.

Why do we combine the axis and rate of rotation into a single vector? Doing so gives us a single vector quantity that is easy to manipulate just like velocity for linear motion. We can easily add and subtract changes to angular velocity to change how the object is rotating just like we can add and subtract from linear velocity. If we stuck with a unit length vector and scalar for rotation speed then it would be much more complicated to apply these changes.

But there is one very important difference between linear and angular velocity. Unlike linear velocity, there is no guarantee that angular velocity will remain constant over time in the absence of forces. In other words, angular momentum is conserved while angular velocity is not. This means that we cannot trust angular velocity as a primary value and we need to use angular momentum instead.

Angular Momentum, Inertia and Torque

Just as velocity and momentum are related by mass in linear motion, angular velocity and angular momentum are related by a quantity called the rotational inertia. This tensor is a measurement of how much effort it takes to spin an object around an axis. It depends on both the shape of the object and how much it weighs.

In the general case, rotational inertia is represented by a 3x3 matrix called an inertia tensor. Here we make a simplifying assumption by discussing physics in the context of simulating a cube. Because of the symmetries of the cube, we only need a single value for the rotational inertia: 16 x size^2 x mass, where size is the length of the sides of the cube.

Just as we integrate linear momentum from force, we integrate angular momentum directly from the rotational equivalent of force called torque. You can think of torque just like a force, except that when it is applied it induces a rotation around an axis in the direction of torque vector rather than accelerating the object linearly. For example, a torque of (1,0,0) would cause a stationary object to start rotating about the x axis.

Once we have angular momentum integrated, we multiply it by the inverse of the rotational inertia to get the angular velocity, and using this angular velocity we integrate to get the rotational equivalent of position called orientation.

However, as we will see, integrating orientation from angular velocity is a bit more complicated!

Orientation in 3D

This complexity is due to the difficulty of representing orientations in three dimensions.

In two dimensions orientations are easy, you just keep track of an angle in radians and you are done. In three dimensions it becomes much more complex. It turns out that you must either use 3x3 rotation matrices or quaternions to correctly represent the orientation of an object.

For reasons of simplicity and efficiency I’m going to use quaternions to represent the orientation instead of matrices. This also gives us an easy way to interpolate between the previous and current physics orientation to get smooth framerate independent animation as per the time stepping scheme outlined in the previous article.

Now there are plenty of resources on the internet which explain what quaternions are and how unit length quaternions are used to represent rotations in three dimensions. Here is a particularly nice one. What you need to know however is that, effectively, unit quaternions represent an axis of rotation and an amount of rotation about that axis. This may seem similar to our angular velocity, but quaternions are four dimensional vectors instead of three, so mathematically they are actually quite different!

We will represent quaternions in code as another struct:

If we define the rotation of a quaternion as being relative to an initial orientation of the object (what we will later call body coordinates) then we can use this quaternion to represent the orientation of the object at any point in time. Now that we have decided on the representation to use for orientation, we need to integrate it over time so that the object rotates according to the angular velocity.

Integrating Orientation

We are now presented with a problem. Orientation is a quaternion but angular velocity is a vector. How can we integrate orientation from angular velocity when the two quantities are in different mathematical forms?

The solution is to convert angular velocity into a quaternion form, then to use this quaternion to integrate orientation. For lack of a better term I will call this time derivative of orientation “spin”. Exactly how to calculate this spin quaternion is described in detail here.

Here is the final result:

Where q is the current orientation quaternion, and w is the current angular velocity in quaternion form (0,x,y,z) such that x, y, z are the components of the angular velocity vector. Note that the multiplication done between w and q is quaternion multiplication.

To implement this in code we add spin as a new secondary quantity calculated from angular velocity in the recalculate method. We also add spin to the derivatives struct as it is the derivative of orientation:

Integrating a quaternion, just like integrating a vector, is as simple as doing the integration for each value separately. The only difference is that after integrating orientation we must renormalize the orientation quaternion to make it unit length, to ensure that it still represents a rotation.

This is required because errors in integration accumulate over time and make the quaternion ‘drift’ away from being unit length. I like to renormalize in the recalculate method for simplicity, but you can get away with doing it less frequently if cpu cycles are tight.

Now in order to drive the rotation of the object, we need a method that can calculate the torque applied given the current rotational state and time just like the force method we use when integrating linear motion. eg:

This function returns an acceleration torque to induce a spin around the x axis, but also applies a damping over time so that at a certain speed the accelerating and damping will cancel each other out. This is done so that the rotation will reach a certain rate and stay constant instead of getting faster and faster over time.

Combining Linear and Angular Motion

Now that we are able to integrate linear and rotational effects, how can they be combined into one simulation? The answer is to just integrate the linear and rotational physics state separately and everything works out. This is because the objects we are simulating are rigid so we can decompose their motion into separate linear and rotational components. As far as integration is concerned, you can treat linear and angular effects as being completely independent of each other.

Now that we have an object that is translating and rotating through three dimensional space, we need a way to keep track of where it is. We must now introduce the concepts of body coordinates and world coordinates.

Think of body coordinates in terms of the object in a convenient layout, for example its center of mass would be at the origin (0,0,0) and it would be oriented in the simplest way possible. In the case of the simulation that accompanies this article, in body space the cube is oriented so that it lines up with the x, y and z axes and the center of the cube is at the origin.

The important thing to understand is that the object remains stationary in body space, and is transformed into world space using a combination of translation and rotation operations which put it in the correct position and orientation for rendering. When you see the cube animating on screen it is because it is being drawn in world space using the body to world transformation.

We have the raw materials to implement this transform from body coordinates into world coordinates in the position vector and the orientation quaternion. The trick to combining the two is to convert each of them into 4x4 matrix form which is capable of representing both rotation and translation. Then we combine the two transformations into a single matrix by multiplication. This combined matrix has the effect of first rotating the cube around the origin to get the correct orientation, then translating the cube to the correct position in world space. See this article for details on how this is done.

If we then invert this matrix we get one that has the opposite effect, it transforms points in world coordinates into the body coordinates of the object. Once we have both these matrices we have the ability to convert points from body to world coordinates and back again which is very handy. These two matrices become new secondary values calculated in the ‘recalculate’ method from the orientation quaternion and position vector.

Forces and Torques

We can apply separate forces and torques to an object individually, but we know from real life that if we push an object it usually makes it both move and rotate. So how can we break down a force applied at a point on the object into a linear force which causes a change in momentum, and a torque which changes angular momentum?

Given that our object is a rigid body, what actually happens here is that the entire force applied at the point is applied linearly, plus a torque is also generated based on the cross product of the force vector and the point on the object relative to the center of mass of the object:

Flinear = FFtorque = F x (p - x)

Where F is the force being applied at point p in world coordinates, and x is the center of mass of the object.

This seems counterintuitive at first. Why is the force being applied twice? Once to linear and once to rotational motion?

What is happening here is our everyday experience with objects clouding the true behavior of an object under ideal conditions.

Remember your pushbike when you were a kid? You would have to change your tire and flip the bike upside down. You could spin the tire around by pushing on it. You don’t see any linear motion here, just rotation, so what is going on? The answer of course is that the axle of the wheel is counteracting the linear component of the force you applied, leaving only the rotational component. Not convinced? Imagine what would happen if you tried to ride your bike without an axle in your wheel…

Another example: consider a bowling ball lying on a slippery surface such as ice so that no significant friction is present. Now in your mind try to work out a way that you can apply a force at a single point on the surface of the bowling ball such that it will stay completely still while rotating on the spot. There is no way you can do this! Any point where you push would also make the bowling ball move linearly as well as rotate. To apply a pure rotation you’d have to push on both sides of the ball, canceling the linear component of your force out leaving only torque.

So remember, whenever you apply a force to an object there will always be a linear force component which causes the object to accelerate linearly, as well as, depending on the direction of the force, a rotational component that causes the object to rotate.

Velocity at a Point

The final piece of the puzzle is how to calculate the velocity of a single point in the rigid body. To do this we start with the linear velocity of the object, because all points must move with this velocity to keep it rigid, then add the velocity at the point due to rotation.

This velocity due to rotation will not be constant for every point in the body if it is rotating, as each point in the body must be spinning around the axis of rotation. Combining the linear and angular velocities, the total velocity of a point in the rigid body is:

vpoint = vlinear + vangular cross (p - x)

Where p is the point on the rigid body and x is the center of mass of the object.


We have covered the techniques required to simulate linear and rotational movement of a rigid body in three dimensions. By combining the linear and rotational physics into a single physics state and integrating, we can simulate the motion of a rigid body in three dimensions.

NEXT ARTICLE:Spring Physics

In What's in a Projectile Physics Engine, we covered the theory and essential elements of physics engines that can be used to simulate projectile effects in games like Angry Birds. Now, we'll cement that knowledge with a real example. In this tutorial, I'll break down the code for a simple physics-based game that I've written, so you can see exactly how it works.

For those interested, the example code provided throughout this tutorial uses the Sprite Kit API provided for native iOS games. This API uses an Objective-C wrapped Box2D as the physics simulation engine, but the concepts and their application can be used in any 2D physics engine or world.

Building a Game World

Here is the sample game in action:

The overall concept of the game takes the following form:

  1. A structure of platforms with physics bodies are added to the level, building a tower.
  2. One or more objective objects are placed within the tower, each with a physics body assigned to it.
  3. A firing mechanism shoots a projectile body with an momentary impulse; when the projectile's body collides with the platforms' bodies, the simulation takes over and computes the results for us.
  4. If a projectile or a platform touches the objective, it fades from the scene, and the player wins! This collision is detected using the physics bodies, so that the simulation maintains its realism at the point of collision.

Our first use of physics will be to create an edge loop body around our screen's frame. The following is added to an initializer or  method:

//create an edge-loop physics body for the screen, basically creating a "bounds" self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];

This will keep all of our objects within the frame, so that gravity won't pull our whole game off the screen!

Adding Objects

Let's look at adding some physics-enabled sprites to our scene. First, we will look at the code for adding three types of platforms. We will use square, rectangular, and triangular platforms to work with in this simulation.

-(void)createPlatformStructures:(NSArray*)platforms { for (NSDictionary *platform in platforms) { //Grab Info From Dictionay and prepare variables int type = [platform[@"platformType"] intValue]; CGPoint position = CGPointFromString(platform[@"platformPosition"]); SKSpriteNode *platSprite; platSprite.zPosition = 10; //Logic to populate level based on the platform type if (type == 1) { //Square platSprite = [SKSpriteNode spriteNodeWithImageNamed:@"SquarePlatform"]; //create sprite platSprite.position = position; //position sprite platSprite.name = @"Square"; CGRect physicsBodyRect = platSprite.frame; //build a rectangle variable based on size platSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:physicsBodyRect.size]; //build physics body platSprite.physicsBody.categoryBitMask = otherMask; //assign a category mask to the physics body platSprite.physicsBody.contactTestBitMask = objectiveMask; //create a contact test mask for physics body contact callbacks platSprite.physicsBody.usesPreciseCollisionDetection = YES; } else if (type == 2) { //Rectangle platSprite = [SKSpriteNode spriteNodeWithImageNamed:@"RectanglePlatform"]; //create sprite platSprite.position = position; //position sprite platSprite.name = @"Rectangle"; CGRect physicsBodyRect = platSprite.frame; //build a rectangle variable based on size platSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:physicsBodyRect.size]; //build physics body platSprite.physicsBody.categoryBitMask = otherMask; //assign a category mask to the physics body platSprite.physicsBody.contactTestBitMask = objectiveMask; //create a contact test mask for physics body contact callbacks platSprite.physicsBody.usesPreciseCollisionDetection = YES; } else if (type == 3) { //Triangle platSprite = [SKSpriteNode spriteNodeWithImageNamed:@"TrianglePlatform"]; //create sprite platSprite.position = position; //position sprite platSprite.name = @"Triangle"; //Create a mutable path in the shape of a triangle, using the sprite bounds as a guideline CGMutablePathRef physicsPath = CGPathCreateMutable(); CGPathMoveToPoint(physicsPath, nil, -platSprite.size.width/2, -platSprite.size.height/2); CGPathAddLineToPoint(physicsPath, nil, platSprite.size.width/2, -platSprite.size.height/2); CGPathAddLineToPoint(physicsPath, nil, 0, platSprite.size.height/2); CGPathAddLineToPoint(physicsPath, nil, -platSprite.size.width/2, -platSprite.size.height/2); platSprite.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:physicsPath]; //build physics body platSprite.physicsBody.categoryBitMask = otherMask; //assign a category mask to the physics body platSprite.physicsBody.contactTestBitMask = objectiveMask; //create a contact test mask for physics body contact callbacks platSprite.physicsBody.usesPreciseCollisionDetection = YES; CGPathRelease(physicsPath);//release the path now that we are done with it } [self addChild:platSprite]; } }

We'll get to what all the property declarations mean in a bit. For now, focus on the creation of each body. The square and the rectangular platforms each create their bodies in a one line declaration, using the sprite's bounding box as the body size. The triangle platform's body requires drawing a path; this also uses the sprite's bounding box, but calculates a triangle at the corners and halfway points of the frame.

The objective object, a star, is similarly created, but we will use a circular physics body.

-(void)addObjectives:(NSArray*)objectives { for (NSDictionary* objective in objectives) { //Grab the position information from the dictionary provided from the plist CGPoint position = CGPointFromString(objective[@"objectivePosition"]); //create a sprite based on the info from the dictionary above SKSpriteNode *objSprite = [SKSpriteNode spriteNodeWithImageNamed:@"star"]; objSprite.position = position; objSprite.name = @"objective"; //Assign a physics body and physic properties to the sprite objSprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:objSprite.size.width/2]; objSprite.physicsBody.categoryBitMask = objectiveMask; objSprite.physicsBody.contactTestBitMask = otherMask; objSprite.physicsBody.usesPreciseCollisionDetection = YES; objSprite.physicsBody.affectedByGravity = NO; objSprite.physicsBody.allowsRotation = NO; //add the child to the scene [self addChild:objSprite]; //Create an action to make the objective more interesting SKAction *turn = [SKAction rotateByAngle:1 duration:1]; SKAction *repeat = [SKAction repeatActionForever:turn]; [objSprite runAction:repeat]; } }

Ready, Set, Fire!

The cannon itself doesn't need any bodies attached, as it has no need for collision detection. We will simply use it as a starting point for our projectile. 

Here is the method for creating a projectile:

-(void) addProjectile { //Create a sprite based on our image, give it a position and name projectile = [SKSpriteNode spriteNodeWithImageNamed:@"ball"]; projectile.position = cannon.position; projectile.zPosition = 20; projectile.name = @"Projectile"; //Assign a physics body to the sprite projectile.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:projectile.size.width/2]; //Assign properties to the physics body (these all exist and have default values upon the creation of the body) projectile.physicsBody.restitution = 0.5; projectile.physicsBody.density = 5; projectile.physicsBody.friction = 1; projectile.physicsBody.dynamic = YES; projectile.physicsBody.allowsRotation = YES; projectile.physicsBody.categoryBitMask = otherMask; projectile.physicsBody.contactTestBitMask = objectiveMask; projectile.physicsBody.usesPreciseCollisionDetection = YES; //Add the sprite to the scene, with the physics body attached [self addChild:projectile]; }

Here we see a more complete declaration of some properties assignable to a physics body. When playing with the sample project later, try altering the , , and of the projectile to see what effects they have on the overall gameplay. (You can find definitions for each property in What's in a Projectile Physics Engine?)

The next step is to create the code to actually shoot this ball at the target. For this, we'll apply an impulse to a projectile based on a touch event:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { /* Called when a touch begins */ for (UITouch *touch in touches) { CGPoint location = [touch locationInNode:self]; NSLog(@"Touched x:%f, y:%f", location.x, location.y); //Check if there is already a projectile in the scene if (!isThereAProjectile) { //If not, add it isThereAProjectile = YES; [self addProjectile]; //Create a Vector to use as a 2D force value projectileForce = CGVectorMake(18, 18); for (SKSpriteNode *node in self.children){ if ([node.name isEqualToString:@"Projectile"]) { //Apply an impulse to the projectile, overtaking gravity and friction temporarily [node.physicsBody applyImpulse:projectileForce]; } } } } }

Another fun alteration to the project might be to play with the impulse vector value. Forces—and therefore impulses—are applied using vectors, giving magnitude and direction to any force value.

Now we have our structure and our objective, and we can shoot at them, but how do we see if we scored a hit?

Collision Course

First, a quick pair of definitions:

  • A contact is used when two bodies touch.
  • A collision is used to prevent two bodies from intersecting.

Contact Listener

So far, the physics engine has been handling contacts and collisions for us. What if we wanted to do something special when two particular objects touch? To start with, we need to tell our game that we want to listen for the contact. We will use a delegate and a declaration to accomplish this. 

We add the following code to the top of the file:

@interface MyScene ()<SKPhysicsContactDelegate> @end

...and add this statement to the initializer:

This allows us to use the method stub depicted below to listen for contact:

-(void)didBeginContact:(SKPhysicsContact *)contact { //code }

Before we can use this method, though, we need to discuss categories.


We can assign categories to our various physics bodies, as a property, to sort them into groups. 

Sprite Kit in particular uses bit-wise categories, meaning we are limited to 32 categories in any given scene. I like to define my categories using a static constant declaration like this:

//Create Physics Category Bit-Mask's static const uint32_t objectiveMask = 1 << 0; static const uint32_t otherMask = 1 << 1;

Note the use of bit-wise operators in the declaration (a discussion on bitwise operators and bit variables is beyond the scope of this tutorial; just know that they are essentially just numbers stored in a very quickly accessed variable, and that you can have 32 maximum).

We assign the categories using the following properties:

platSprite.physicsBody.categoryBitMask = otherMask; //assign a category mask to the physics body platSprite.physicsBody.contactTestBitMask = objectiveMask; //create a contact test mask for physics body contact callbacks

Doing the same for the other variables in the project, let's us now complete our contact listener method stub from earlier, and also this discussion!

-(void)didBeginContact:(SKPhysicsContact *)contact { //this is the contact listener method, we give it the contact assignments we care about and then perform actions based on the collision uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask); //define a collision between two category masks if (collision == (otherMask| objectiveMask)) { //handle the collision from the above if statement, you can create more if/else statements for more categories if (!isGameReseting) { NSLog(@"You Win!"); isGameReseting = YES; //Set up a little action/animation for when an objective is hit SKAction *scaleUp = [SKAction scaleTo:1.25 duration:0.5]; SKAction *tint = [SKAction colorizeWithColor:[UIColor redColor] colorBlendFactor:1 duration:0.5]; SKAction *blowUp = [SKAction group:@[scaleUp, tint]]; SKAction *scaleDown = [SKAction scaleTo:0.2 duration:0.75]; SKAction *fadeOut = [SKAction fadeAlphaTo:0 duration:0.75]; SKAction *blowDown = [SKAction group:@[scaleDown, fadeOut]]; SKAction *remove = [SKAction removeFromParent]; SKAction *sequence = [SKAction sequence:@[blowUp, blowDown, remove]]; //Figure out which of the contact bodies is an objective by checking its name, and then run the action on it if ([contact.bodyA.node.name isEqualToString:@"objective"]) { [contact.bodyA.node runAction:sequence]; } else if ([contact.bodyB.node.name isEqualToString:@"objective"]) { [contact.bodyB.node runAction:sequence]; } //after a few seconds, restart the level [self performSelector:@selector(gameOver) withObject:nil afterDelay:3.0f]; } } }


I hope that you've enjoyed this tutorial! We have learned all about 2D physics and how they can be applied to a 2D projectile game. I hope you now have a better understanding of what you can do to start using physics in your own games, and how physics can lead to some new and fun gameplay. Let me know in the comments below what you think, and if you use anything you've learned here today to create projects of your own, I'd love to hear about it. 

A Note on the Example Project

I have included a working example of the code provided in this project as a GitHub repo. The fully commented source code is there for all to use. 

Some minor portions of the working project unrelated to physics were not discussed in this tutorial. For instance, the project is built to be expandable, so the code allows the loading of multiple levels using a property list file to create different platform arrangements and multiple objectives to hit. The game over section, and the code to remove objects and timers, were also not discussed, but are fully commented and available within the project files.

Some ideas for features you could add to expand on the project:

  1. Different types of ammo
  2. Movable and scalable shot direction and magnitude
  3. More types and sizes of platforms
  4. Terrain
  5. Animation and sound effects 

Have fun! Thanks for reading!

0 Thoughts to “3d Game Physics Tutorial Homework

Leave a comment

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *