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.
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
) representsclass::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:
- What is Newton’s second law of motion? Do not just write F = ma; explain the intuition behind it. 2 . What is momentum? Impulse?
- What is a perfectly elastic collision? Give a real-life example of a (nearly) perfectly elastic collision.
- What is a perfectly inelastic collision? Give a real-life example of a (nearly) perfectly inelastic collision.
- What is the coefficient of restitution? What range of values can it take?
- What is angular velocity? Angular acceleration?
- What is the moment of inertia? Angular momentum?
- What is torque?
- 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 centerPART_LEFT_ARM
: The vector offset of the left arm center from the torso centerPART_RIGHT_ARM
: The vector offset of the right arm center from the torso centerPART_LEFT_FOREARM
: The vector offset of the left forearm center from the arm centerPART_RIGHT_FOREARM
: The vector offset of the right forearm center from the arm centerPART_LEFT_THIGH
: The vector offset of the left thigh center from the torso centerPART_RIGHT_THIGH
: The vector offset of the right thigh center from the torso centerPART_LEFT_SHIN
: The vector offset of the left shin center from the left thight centerPART_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