Physics Engine Integration

For this lab, you will get your hands dirty with Box2d, a popular 2D physics engine. While the API is a little strange, it is one of the most robust, efficient physics engines you can use for your 2D game. This is the physics engine that is used by almost all 2D Indie Games.

image

Once again, this lab is not due during this lab period, but it is to your advantage to work through (and/or learn) as much as you can now, while assistance is immediately available. If you are experiencing any problems, contact one of the programming TAs.

Table of Contents


Game Rules

Download: PhysicsSoln.jar

This “game” is actually three different minigames showing off the different ways you can use physics in a game. Once you complete one minigame, it immediately takes you to the next one. In addition, you can press ‘n’ (for Next) and ‘p’ (for Previous) to navigate through the minigames at will. Pressing ‘r’ will reset the current game.

We have also provided you with a precompiled solution. Do not decompile the solution. We know how easy it is to decompile Java (and have caught many people with Moss in the introductory courses). Doing so is a violation of the Academic Integrity policy of the course. Do not be that person.

The Mini-Games

There are three different minigames in this lab.

Rocket Lander

The first mini game is a rocket ship in a room with crates and an exit. The game is over when the ship reaches the exit. Currently, the rocket ship has no thrusters. It is up to you to apply forces to the ship so that you can guide it to the exit.

Platformer

The second minigame is a platformer world where you need to guide your player to another exit. Your character can run (left and right arrows), jump (up arrow) and shoot (space bar). He needs to somehow get over the initial gap, jump up on the platforms to the right, and get back over the gap to the exit.

There used to be rope bridge over the gap, but it is falling apart. You need to repair it. There is also a spinning platform near the exit that you can spin (by shooting it) to give you just enough room to jump to the exit. You need to add a joint to keep the platform from falling.

Rag Doll

The last minigame is a fishtank with a ragdoll of Professor White. Currently, he is in pieces after an unfortunate shark-related snorkeling incident. You need to reassemble this ragdoll so that it is all in one piece. The result is a sandbox game where you can drag the ragdoll, but there is no way to win.


Project Files

Download: PhysicsLab.zip

The code for this lab is some of the most complicated that you have seen so far. There are over 20 classes and multiple packges. In fact, each minigame is grouped it its own package. The top-level classes provided the outer game engine, but most of the work is done by the classes in the individual folders.

We have put a lot of work into this lab because it is our experience that everyone uses this lab as a template for their gameplay prototype (and some even build off this for their technical prototype). This absolutely acceptable. Just remember that, when you borrow code for a project, you must cite its origin in your comments.

Code Organization

There are way too many classes for us to give a dependency diagram this time. Furthermore, you will notice that there is a lot of subclassing in the package edu.cornell.gdiac.physics.obstacles package. This degree of subclassing is actually in direct contradiction to some of the lectures that we are about to have in class. We have done this to hide as much of the physics engine as possible from you. All of those classes take care of the more complicated Box2d details, such as Shape and Fixture classes. All you need to do is focus on the Body class.

With that said, a lot of this code should look familiar. There is a GameCanvas for drawing and a GDXRoot. We have a controller for processing input and model classes. And all of the minigame folders are organized with roughly the same types of classes. The only thing that is missing is the CollisionController from the past two assignments. That is because Box2d handles that detail, so it is imbedded in the root class for each game mode.

With all that said, there are some important classes to pay attention to.

GameCanvas

You should be very familiar with the class by now. The structure is much the same as before. However, this is a general purpose version of GameCanvas that should be enough for the vast majority of games in this class. If you need more complicated graphics than this, talk to a student that has taken CS 4620.

You will note that this canvas has two passes: a standard pass and debug pass. The standard pass draws sprites while the debug pass draws colored outlines of boxes. The purpose of the debug pass is to show you the outline of the physics bodies which may or may not agree with the sprites (e.g. there is no such thing as a circular sprite). You can turn this mode on or off by pressing the ‘d’ key.

WorldController

This controller class is essentially CollisionController and GameplayController rolled up into a single class. Each minigame has its own subclass of WorldController. This subclass acts like is own game engine; it initializes the game world, processes the game loop, and draws the objects to the game canvas. The game loop handles input, all player actions, the game physics, and any garbage collection necessary. In short, this class and its subclasses are the ones you should be looking at first.

The primary field in WorldController is world. This is a Box2d World object that stores all of the active physics objects. When you add a new physics object, or create new joints, you have to do it with this object. When an object is destroyed, you must manually delete it from this World or it will hang around, continuing to collide with other objects. Indeed, this is what our “garbage collection” code does: remove deleted objects from the World object.

When people adapt this lab for their game, they often keep this WorldController around. However, note that we only have this class because we have three minigames. Otherwise, we would embed this code in the appropriate gameplay controller (e.g. RocketController).

BoxObstacle

The base model class is Obstacle, but that is an abstract class that does not do a lot of things. Its immediate subclass SimpleObstacle is more useful, since it actually has a Box2d Body attached. But that class does not have a shape (it is a physics body with motion but no form). BoxObstacle is the simplest physics object with a shape. It is also the physics object that you will be using 90% of the time.

You should familiarize yourself with all of the attributes in this class. You may need to look at the subclasses to do this. You should always access the attributes with the getters and setters. That is because the behavior of the getters and setters differs depending on whether the physics engine is live or not. Accessing attributes directly will result in a lot of bugs.

The most important getter is getBody(), which accesses the Box2D physics body. You will need to access this property regularly to add joints and apply forces.

As in the previous three labs, we are sticking with passive models; they have getters and setters but not much else. While this class has an update method, we do not put a lot of gameplay code in this method. In fact, if you look at the subclasses RocketModel and DudeModel the only gameplay code that we put in this method is managing cooldowns. The update method is really there to simplify physics handling, since the Fixture object must be deleted and recreated whenever you resize the shape.

ComplexObstacle

A complex physics object is a collection of other physics objects connected by joints. This is the subclass for RopeBridge and Ragdoll classes. Understanding how to use joints is a major part of this lab. The only thing you need to know about this class is the method createJoints.


Box2d Basics

The most difficult part of this lab is going to be reading the manual. The LibGDX API for Box2d is complete but not fully documented. Your instructor had to look at the source code in the GitHub for LibGDX a few times to get this lab right. You you will to look at both the LibGDX API and the Box2d manual.

The manual for Box2d is very good. However, the problem here is that it is written for C++, which is he native language for this library. As a result, you may find yourself going back and forth between the two links above quite a bit. Here are a few guidelines for to help you understand the manual.

  • In C++, all classes are prefixed b2; these are removed in LibGDX. For example b2Body is Body in Java.
  • Pointer references (either * or & attached to a variable) should be ignored. Java does not have pointers.
  • All instances of -> should be replaced by a period (.). Again, this is because Java does not have pointers.
  • A :: (e.g. b2World::CreateBody) represents class::method.

If you see anything else that you do not understand, please ask a TA or post your question on Ed Discussions.

Box2d Tutorials

This is an extremely popular physics engine. If you get stuck there are lots of resource online to help you. Here are just a few that we have found.

The key thing to keep in mind is the classes that end in Def (e.g. BodyDef, FixtureDef and JointDef. You do not make a new Body or Joint with new. You must allocate it through the World object. The World object uses these Def objects to initialize the object when it allocates them. Essentially Box2d has an internal malloc that it uses instead of the one for your programming language. For example, to create a new physics body, you need to do the following at a minimum:

  BodyDef def = new BodyDef();
  Body body = world.createBody(def);

If you look at our code in RopeBridge, you will get a better idea of how this works.


Lab Instructions

This lab is broken into five parts. It is a combination of writing code and answering questions. The coding is often the simplest part of the assignment. The questions are more important, as they demonstrate that you have a basic understanding of physics and know how to use the Box2d engine. And if you forgotten your physics, this lab acts as a bit of a refresher.

Basic Physics Questions

Most of things should be familiar from your high school physics class (e.g. you do not need Calculus). For others, you may need to do some online hunting. Chris Hecker’s Rigid Body Dynamics articles are a particularly useful resource. If cannot figure out a question, please ask a TA.

You should answer each of the following questions in 20 words or less:

  1. What is Newton’s second law of motion? Do not just write F = ma; explain the intuition behind it. 2 . What is momentum? Impulse?
  2. What is a perfectly elastic collision? Give a real-life example of a (nearly) perfectly elastic collision.
  3. What is a perfectly inelastic collision? Give a real-life example of a (nearly) perfectly inelastic collision.
  4. What is the coefficient of restitution? What range of values can it take?
  5. What is angular velocity? Angular acceleration?
  6. What is the moment of inertia? Angular momentum?
  7. What is torque?
  8. What is the relationship between torque, moment of inertia, and angular acceleration?

You should write your answers in a text file called readme.txt, which you will submit as part of this assignment.

Box2d Questions

The next set of questions require that you read both the Box2D Documentation and the LibGDX API Remember to refer to our translation guide when trying to read the Box2D API (which is in C++).

For each of these questions. we try to make it clear which of the two manuals we are refering to. You should answer each question in a few sentences:

  • In Box2d, what is the difference between a Shape and a Body?
  • Between a Shape and a Body, which do you go to to change a physical property? To apply a force?
  • When would you want a Body to contain multiple Shapes?
  • In Box2d, what is a World? What are some of its important properties?
  • What is the advantage of sleeping an object with a Body? When would you want to do this?
  • In Box2d, what is the difference between a static body and a dynamic body? How do you specify which type a body is?
  • In Box2d, what is a Bullet and when do you want an object to be one?
  • In LibGDX, what can you do with a ContactListener inside of a World? How does this help with sensors?
  • In LibGDX, what are the steps that you must take to add a Shape to a Body?

You should add your answers to the file readme.txt

Forces

After you have answered the physics questions, you are ready to start work on the minigames. The first minigame involves controlling a rocket ship in a land of crates. For this part os the assignment you will need to modify two different files: RocketModel.java and RocketController.java, Each serves a different role. Look at the comments in the source code to see what is going on. We talk about each of these classes (or at least their subclasses) in our code organization overview

All of the code that you write should go inside of the regions marked INSERT CODE HERE. You should never need to write code outside of these regions.

1. Move the Rocket

The rocket needs to be navigated to the exit but there is currently no way to move it! You need to add code in two places. In RocketController you need to convert the player input into a force (remember to use the getThrust() method), and put that force inside the RocketModel. Once you assign the force, you should call the method applyForce to move the rocket.

The method applyForce in RocketModel is itself unfinished. We perform a translation on the force for you (to include the angle of the ship). However, we do not apply the force to the Box2d body. You must do this. If you are unsure of this step, look at the code in PlatformController and DudeModel. It is very close to what we are asking for.

2. Stabilize the Rocket

Now that the rocket is moving, you’ll notice it is hard to control because it will rotate upon crashing into things. Add code to prevent the rocket from rotating in RocketModel’s activatePhysics method. This should take no more than one line of code. Do not remove any code that came with the lab.

Ropes and Joints

In the second minigame, you need to walk on foot to the exit. This is a classic platformer where you can move around with the left and right arrow keys, jump with the up key, and shoot bullets with space. However, the bridge is out and the rotating platform is not connected to anything.

For this part of the lab, you will need to consult the Box2D Documentation to learn about joints. Remember to consult our translation hints if you need help understanding the C++ API.

3. Fix the Rope Bridge

Currently the planks of the bridge are not connected to each other and just fall into the pit. We have attached the bridge for you at the end points, but we have not attached the planks in between. Implement the joints in RopeBridge so that the bridge is crossable.

You might want to look into Revolute Joints in the Box2d API to get an idea as to how to connect the bridge together. The bridge should behave similarly to the solution (i.e. it should not be completely rigid).

4. Fix the Spinning Platform

When the level start, you will notice that a large pole falls from the sky. This pole is provided by the class Spinner. While it is a ComplexObstacle like the bridge, right now it is just a BoxObstacle. This should give you a hint about what is wrong.

You need to do two things in this class: (1) add a pivot for it to spin around, and (2) a joint that connects the spinner to the pivot. Your solution should allow the spinner to stay fixed in space and rotates. To get an idea of how this should operate, consider looking at the solution executable. For an idea on how to implement this, go look RopeBridge again. Noe that the code is split over two different places.

In addition to writing the code for the solution, you should explain in readme.txt why you cannot just set body type to BodyType.StaticBody, like we do the other platforms. If you are unsure of the answer, try this out (without a joint) and see what happens.

Ragdoll

In a random twist, Professor White has become completely discombobulated in a shark-related snorkeling incident! To put him back together, you should do the last two activities.

5. Assemble the Ragdoll

As with the RopeBridge, you should use joints to create a ragdoll out of his body parts. You will notice a diagram in the comments of RagdollModel detailing which parts should be connected by joints. You will add these joints to the same method that you did for the rope bridge.

As referenced in the documentation for the RagdollModel.java, the ragdoll has the following structure:

o = joint
                  ___
                 |   |
                 |_ _|
  ______ ______ ___o___ ______ ______
 |______o______o       o______o______|
               |       |
               |       |
               |_______|
               | o | o |
               |   |   |
               |___|___|
               | o | o |
               |   |   |
               |   |   |
               |___|___|

When computing the location of these joints, you take the centers and separate their centers by an offset vector (e.g. if the vector is zero, the centers are glued on top of each other). These offsets are stored in the JSON file, which is in turn stored in the variable data. Look at the code creating the bodies in makePart and the constructor to see how access these values. For example, to compute the offsets for left forearm, you use

x = data.get( BODY_PARTS[partToAsset( PART_LEFT_FOREARM )] ).getFloat( 0 );
y = data.get( BODY_PARTS[partToAsset( PART_LEFT_FOREARM )] ).getFloat( 1 );

You are querying the JSON for that body part, which gives a float array of two elements. The result of partToAsset is the index for the correct JSON key in BODY_PARTS. You then access the first for the x-coordinate of the offset and the second for the y-coordinate of the offset. The reason for this approach is because different body parts may share the same textures (and hence the same offsets).

The constants for these body part offsets are as follows:

  • PART_BODY: The vector offset of the torso center from the face center
  • PART_LEFT_ARM: The vector offset of the left arm center from the torso center
  • PART_RIGHT_ARM: The vector offset of the right arm center from the torso center
  • PART_LEFT_FOREARM: The vector offset of the left forearm center from the arm center
  • PART_RIGHT_FOREARM: The vector offset of the right forearm center from the arm center
  • PART_LEFT_THIGH: The vector offset of the left thigh center from the torso center
  • PART_RIGHT_THIGH: The vector offset of the right thigh center from the torso center
  • PART_LEFT_SHIN: The vector offset of the left shin center from the left thight center
  • PART_RIGHT_SHIN: The vector offset of the right shin center from the right thigh center

Remember that joint coordinates are relative to the center of body, and are not part of the world coordinates. If you do not understand this issue, please ask a programmingTA.

6. Extra Credit

This last part of the assignment is completely optional, and will earn extra credit (but no more than 5 points). The Box2d engine provides a lot of very cool features, such as gravity controllers, polygon decomposition, dynamic shape destruction, etc. All of these and more can be seen in (and copied from) the LibGDX testbed. For extra credit, incorporate something you think is cool into the fishtank and tell us about it in the file readme.txt.


Submission

Due: Thu, Feb 22 at 11:59 PM

Theoretically, you should have only modified the following classes:

  • RocketModel.java
  • RocketController.java
  • RopeBridge.java
  • Spinner.java
  • RagdollModel.java

However, you may have modified much more in order to complete the extra credit. Whatever the case, you should create a ZIP file with three components:

  • The file readme.txt, containing the written questions for the lav
  • Any source (.java) file that you have modified.
  • An executable Jar of your solution.

Submit this zip file as lab4code.zip to CMS