How-To: 2D Physics with Box2D

Basic Tutorials concerning: GUI, Views, Activites, XML, Layouts, Intents, ...

How-To: 2D Physics with Box2D

Postby metachris » Wed Mar 04, 2009 12:47 pm

Hey guys!

Here is a brief tutorial on how to use the popular 2D physics engine Box2D in an Android Activity.

This is only part 1, including the import of the libraries, creation of the world, adding a few dynamic objects and updating the world. Not included is the drawing part of things, which is subject to a later post, or a post by someone else :)

  • Download jbox2d-2.0.1-library-only.jar and save it anywhere as jbox2d.jar
  • In Eclipse:
  • Create a folder /lib/
  • Right Click on /lib, select Import
  • Select General / File System
  • Choose the directory of jbox2d.jar
  • Import the file jbox2d.jar
  • Right click on the project and select "Properties"
  • Click on "Java Build Path" and select the "Libraries" tab
  • Click on "Add JARs..." and pick the file jbox2d.jar
Now we have everything set up and can write a hello world! This is the exact example from the Box2D User Manual.
  • Initialize a new world
  • Add a ground box
  • Create some dynamic bodies (balls)
  • Simulate the world
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. import org.jbox2d.collision.AABB;
  2. import org.jbox2d.collision.CircleDef;
  3. import org.jbox2d.collision.PolygonDef;
  4. import org.jbox2d.common.Vec2;
  5. import org.jbox2d.dynamics.Body;
  6. import org.jbox2d.dynamics.BodyDef;
  7. import org.jbox2d.dynamics.World;
  8.  
  9. import android.util.Log;
  10.  
  11. public class PhysicsWorld {
  12.     public int targetFPS = 40;
  13.     public int timeStep = (1000 / targetFPS);
  14.     public int iterations = 5;
  15.  
  16.     private Body[] bodies = new Body[100];
  17.     private int count = 0;
  18.    
  19.     private AABB worldAABB;
  20.     private World world;
  21.     private BodyDef groundBodyDef;
  22.     private PolygonDef groundShapeDef;
  23.  
  24.     public void create() {
  25.         // Step 1: Create Physics World Boundaries
  26.         worldAABB = new AABB();
  27.         worldAABB.lowerBound.set(new Vec2((float) -100.0, (float) -100.0));
  28.         worldAABB.upperBound.set(new Vec2((float) 100.0, (float) 100.0));
  29.        
  30.         // Step 2: Create Physics World with Gravity
  31.         Vec2 gravity = new Vec2((float) 0.0, (float) -10.0);
  32.         boolean doSleep = true;
  33.         world = new World(worldAABB, gravity, doSleep);
  34.        
  35.         // Step 3: Create Ground Box
  36.         groundBodyDef = new BodyDef();
  37.         groundBodyDef.position.set(new Vec2((float) 0.0, (float) -10.0));
  38.         Body groundBody = world.createBody(groundBodyDef);
  39.         groundShapeDef = new PolygonDef();
  40.         groundShapeDef.setAsBox((float) 50.0, (float) 10.0);
  41.         groundBody.createShape(groundShapeDef);
  42.     }
  43.  
  44.     public void addBall() {
  45.         // Create Dynamic Body
  46.         BodyDef bodyDef = new BodyDef();
  47.         bodyDef.position.set((float) 6.0+count, (float) 24.0);
  48.         bodies[count] = world.createBody(bodyDef);
  49.        
  50.         // Create Shape with Properties
  51.         CircleDef circle = new CircleDef();
  52.         circle.radius = (float) 1.8;
  53.         circle.density = (float) 1.0;
  54.        
  55.         // Assign shape to Body
  56.         bodies[count].createShape(circle);
  57.         bodies[count].setMassFromShapes();
  58.  
  59.         // Increase Counter
  60.         count += 1;        
  61.     }
  62.    
  63.     public void update() {
  64.         // Update Physics World
  65.         world.step(timeStep, iterations);
  66.        
  67.         // Print info of latest body
  68.         if (count > 0) {
  69.             Vec2 position = bodies[count].getPosition();
  70.             float angle = bodies[count].getAngle();
  71.             Log.v("Physics Test", "Pos: (" + position.x + ", " + position.y + "), Angle: " + angle);
  72.         }
  73.     }    
  74. }
Parsed in 0.040 seconds, using GeSHi 1.0.8.4


To use this in an Activity, you may write a code like this:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. import android.app.Activity;
  2. import android.os.Bundle;
  3. import android.os.Handler;
  4.  
  5. public class Physics extends Activity {
  6.     PhysicsWorld mWorld;
  7.     private Handler mHandler;
  8.  
  9.     @Override
  10.     public void onCreate(Bundle savedInstanceState) {
  11.         super.onCreate(savedInstanceState);
  12.         mWorld = new PhysicsWorld();
  13.         mWorld.create();
  14.  
  15.         // Add 50 Balls
  16.         for (int i=0; i<50; i++) {
  17.             mWorld.addBall();
  18.         }
  19.  
  20.         // Start Regular Update
  21.         mHandler = new Handler();
  22.         mHandler.post(update));
  23.     }
  24.  
  25.     @Override
  26.     protected void onPause() {
  27.         super.onPause();
  28.         mHandler.removeCallbacks(update);
  29.     }
  30.  
  31.     private Runnable update = new Runnable() {
  32.         public void run() {
  33.             mWorld.update();
  34.             mHandler.postDelayed(update, (long) (mWorld.timeStep*1000));
  35.         }
  36.     };
  37. }
Parsed in 0.034 seconds, using GeSHi 1.0.8.4


This is a short version of the full post at 4feets.com. Further suggested reading:


- Chris

[edit]add: bodies=new Body[100];[/edit]
Last edited by metachris on Thu Mar 05, 2009 4:29 pm, edited 2 times in total.
metachris
Junior Developer
Junior Developer
 
Posts: 24
Joined: Sat Feb 07, 2009 11:40 am

Top

Postby torpor » Wed Mar 04, 2009 2:18 pm

Excellent! Nice one Chris - you are rapidly becoming a potent tutorial master! :)

Would be nice to see the next page of this tutorial implement a simple gravity-type game or something .. maybe something for a hack session this week, eh?
torpor
Junior Developer
Junior Developer
 
Posts: 13
Joined: Tue Feb 24, 2009 12:23 pm

Postby grzegorz_ae » Wed Mar 04, 2009 6:26 pm

The best post of the month!
grzegorz_ae
Junior Developer
Junior Developer
 
Posts: 24
Joined: Tue Dec 23, 2008 6:44 pm
Location: Poland Rz-ów

Postby MickArea » Thu Mar 05, 2009 4:29 am

pretty good, but you forget this detail :


bodies=new Body[100];


by example ;)
MickArea
Junior Developer
Junior Developer
 
Posts: 23
Joined: Mon Feb 23, 2009 4:42 pm

Postby metachris » Thu Mar 05, 2009 4:28 pm

MickArea wrote:pretty good, but you forget this detail :


bodies=new Body[100];


by example ;)


Thanks a lot! Fixed :D

If I've got some time, I'll make an example game with Mr. SnowFlakes GameTemplate (which I think is really nice and where I'm making some improvements i'll post about another time).
metachris
Junior Developer
Junior Developer
 
Posts: 24
Joined: Sat Feb 07, 2009 11:40 am

Postby MrSnowflake » Thu Mar 05, 2009 5:59 pm

metachris wrote:I'll make an example game with Mr. SnowFlakes GameTemplate (which I think is really nice and where I'm making some improvements i'll post about another time).
Something that's really nice can't be improved :p. Nice to see people like my simple template. I had some other gaming related stuff in the pipes, but they stayed there since birth :).
User avatar
MrSnowflake
Moderator
Moderator
 
Posts: 1439
Joined: Sat Feb 16, 2008 3:11 pm
Location: Flanders, Belgium

Top

Postby metachris » Thu Mar 05, 2009 9:32 pm

MrSnowflake wrote:Something that's really nice can't be improved :p. Nice to see people like my simple template. I had some other gaming related stuff in the pipes, but they stayed there since birth :).


Release early and often!

:-D

Seriously, I like your work and would certainly love to see some more, even unfinished code!
metachris
Junior Developer
Junior Developer
 
Posts: 24
Joined: Sat Feb 07, 2009 11:40 am

Postby MrSnowflake » Thu Mar 05, 2009 9:52 pm

Somewhere on google code there is a 2D lib of some sorts from me. It's very preliminary, but if you want to see some crappy code from me, go ahead and search for it :).
User avatar
MrSnowflake
Moderator
Moderator
 
Posts: 1439
Joined: Sat Feb 16, 2008 3:11 pm
Location: Flanders, Belgium

Postby MickArea » Fri Mar 06, 2009 1:38 am

hi, now i've something which work, with displaying balls ...
but i'm afraid by the " fluidity " ..... can anyone improve this peace of code ?


PhysicsWorld.java
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.  
  2.  
  3.  
  4. import java.util.Random;
  5.  
  6.  
  7.  
  8. import org.jbox2d.collision.AABB;
  9.  
  10. import org.jbox2d.collision.CircleDef;
  11.  
  12. import org.jbox2d.collision.PolygonDef;
  13.  
  14. import org.jbox2d.common.Vec2;
  15.  
  16. import org.jbox2d.dynamics.Body;
  17.  
  18. import org.jbox2d.dynamics.BodyDef;
  19.  
  20. import org.jbox2d.dynamics.World;
  21.  
  22.  
  23.  
  24. import android.content.Context;
  25.  
  26. import android.graphics.Canvas;
  27.  
  28. import android.graphics.Color;
  29.  
  30. import android.graphics.Paint;
  31.  
  32. import android.graphics.Paint.Style;
  33.  
  34. import android.view.View;
  35.  
  36.  
  37.  
  38. public class PhysicsWorld extends View{
  39.  
  40.        
  41.  
  42.  
  43.  
  44.         protected static final int GUIUPDATEIDENTIFIER = 0x231;
  45.  
  46.        
  47.  
  48.     public int targetFPS = 40;
  49.  
  50.     public float timeStep = 10.0f / targetFPS;
  51.  
  52.     public int iterations = 5;
  53.  
  54.  
  55.  
  56.     private Body[] bodies;
  57.  
  58.     private int count = 0;
  59.  
  60.    
  61.  
  62.     private AABB worldAABB;
  63.  
  64.     public World world;
  65.  
  66.     private PolygonDef groundShapeDef;
  67.  
  68.    
  69.  
  70.     public int World_W,World_H;
  71.  
  72.  
  73.  
  74.         private Paint paint;
  75.  
  76.  
  77.  
  78.         private float radius=10;
  79.  
  80.  
  81.  
  82.  
  83.  
  84.         public PhysicsWorld(Context context,int W,int H) {
  85.  
  86.                 super(context);
  87.  
  88.                 World_W=W;
  89.  
  90.                 World_H=H;
  91.  
  92.         // Step 1: Create Physics World Boundaries
  93.  
  94.         worldAABB = new AABB();
  95.  
  96.        
  97.  
  98.                 Vec2 min = new Vec2(-50, -50);
  99.  
  100.                 Vec2 max = new Vec2(World_W + 50, World_H + 50);
  101.  
  102.  
  103.  
  104.         worldAABB.lowerBound.set(min);
  105.  
  106.         worldAABB.upperBound.set(max);
  107.  
  108.        
  109.  
  110.         // Step 2: Create Physics World with Gravity
  111.  
  112.         Vec2 gravity = new Vec2((float) 0.0, (float) -10.0);
  113.  
  114.         boolean doSleep = true;
  115.  
  116.         world = new World(worldAABB, gravity, doSleep);
  117.  
  118.        
  119.  
  120.        
  121.  
  122.         // Step 3:
  123.  
  124.         //Create Ground Box :
  125.  
  126.         BodyDef bodyDef = new BodyDef();
  127.  
  128.         bodyDef.position.set(new Vec2((float) 0.0, (float) -10.0));
  129.  
  130.         Body groundBody = world.createBody(bodyDef);
  131.  
  132.         groundShapeDef = new PolygonDef();
  133.  
  134.         groundShapeDef.setAsBox((float) World_W, (float) 10);
  135.  
  136.         groundBody.createShape(groundShapeDef);
  137.  
  138.         // up :
  139.  
  140.         bodyDef = new BodyDef();
  141.  
  142.         bodyDef.position.set(new Vec2((float) 0.0, (float) (World_H+10.0) ));
  143.  
  144.         groundBody = world.createBody(bodyDef);
  145.  
  146.         groundShapeDef = new PolygonDef();
  147.  
  148.         groundShapeDef.setAsBox((float) World_W, (float) 10);
  149.  
  150.         groundBody.createShape(groundShapeDef);
  151.  
  152.         // left :
  153.  
  154.         bodyDef = new BodyDef();
  155.  
  156.         bodyDef.position.set(new Vec2((float) -10, (float) 0.0 ));
  157.  
  158.         groundBody = world.createBody(bodyDef);
  159.  
  160.         groundShapeDef = new PolygonDef();
  161.  
  162.         groundShapeDef.setAsBox((float)10, (float) World_H);
  163.  
  164.         groundBody.createShape(groundShapeDef);
  165.  
  166.         // right :
  167.  
  168.         bodyDef = new BodyDef();
  169.  
  170.         bodyDef.position.set(new Vec2((float) World_W+10, (float) 0.0 ));
  171.  
  172.         groundBody = world.createBody(bodyDef);
  173.  
  174.         groundShapeDef = new PolygonDef();
  175.  
  176.         groundShapeDef.setAsBox((float)10, (float) World_H);
  177.  
  178.         groundBody.createShape(groundShapeDef);
  179.  
  180.         //
  181.  
  182.  
  183.  
  184.         // step 4: initialize
  185.  
  186.         bodies=new Body[50];
  187.  
  188.        
  189.  
  190.         //
  191.  
  192.         paint=new Paint();
  193.  
  194.                 paint.setStyle(Style.FILL);
  195.  
  196.                 paint.setColor(Color.RED);
  197.  
  198.     }
  199.  
  200.  
  201.  
  202.     public void addBall() {
  203.  
  204.         // Create Dynamic Body
  205.  
  206.         BodyDef bodyDef = new BodyDef();
  207.  
  208.         Random rnd = new Random();
  209.  
  210.         bodyDef.position.set((float) radius*2+rnd.nextInt( (int)(World_W-radius*4) ), (float)2*radius+ rnd.nextInt( (int)(World_H-radius*4) ));
  211.  
  212.         bodies[count] = world.createBody(bodyDef);
  213.  
  214.        
  215.  
  216.         // Create Shape with Properties
  217.  
  218.         CircleDef circle = new CircleDef();
  219.  
  220.         circle.radius = (float) radius;
  221.  
  222.         circle.density = (float) 1.0;
  223.  
  224.         circle.restitution=0.5f;
  225.  
  226.        
  227.  
  228.         // Assign shape to Body
  229.  
  230.         bodies[count].createShape(circle);
  231.  
  232.         bodies[count].setMassFromShapes();
  233.  
  234.        
  235.  
  236.        
  237.  
  238.        
  239.  
  240.  
  241.  
  242.  
  243.  
  244.  
  245.  
  246.         // Increase Counter
  247.  
  248.         count += 1;        
  249.  
  250.     }
  251.  
  252.    
  253.  
  254.     public void update() {
  255.  
  256.        
  257.  
  258.      
  259.  
  260.        world.step(   timeStep  , iterations);
  261.  
  262.         postInvalidate();
  263.  
  264.     }  
  265.  
  266.     @Override
  267.  
  268.     protected void onDraw(Canvas canvas) {
  269.  
  270.                 // draw balls
  271.  
  272.                 //
  273.  
  274.         for(int j = 0;j<count;j++) {
  275.  
  276.                         canvas.drawCircle(bodies[j].getPosition().x,World_H- bodies[j].getPosition().y, radius, paint);
  277.  
  278.                 }
  279.  
  280.  
  281.  
  282.         }
  283.  
  284. }
  285.  
  286.  
Parsed in 0.052 seconds, using GeSHi 1.0.8.4


Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.  
  2.  
  3.  
  4. import android.app.Activity;
  5.  
  6. import android.os.Bundle;
  7.  
  8. import android.os.Handler;
  9.  
  10. import android.util.DisplayMetrics;
  11.  
  12. import android.view.Window;
  13.  
  14. import android.view.WindowManager;
  15.  
  16.  
  17.  
  18. public class Physics extends Activity   {
  19.  
  20.     PhysicsWorld mWorld;
  21.  
  22.     private Handler mHandler;
  23.  
  24.        
  25.  
  26.     @Override
  27.  
  28.     public void onCreate(Bundle savedInstanceState) {
  29.  
  30.         super.onCreate(savedInstanceState);
  31.  
  32.         getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
  33.  
  34.         this.requestWindowFeature(Window.FEATURE_NO_TITLE);
  35.  
  36.        
  37.  
  38.        
  39.  
  40.                 DisplayMetrics dm = new DisplayMetrics();
  41.  
  42.                 getWindowManager().getDefaultDisplay().getMetrics(dm);
  43.  
  44.                
  45.  
  46.         mWorld = new PhysicsWorld(this,dm.widthPixels,dm.heightPixels);
  47.  
  48.         this.setContentView(this.mWorld);
  49.  
  50.  
  51.  
  52.         // Add 10 Balls
  53.  
  54.         for (int i=0; i<10; i++) {
  55.  
  56.             mWorld.addBall();
  57.  
  58.         }
  59.  
  60.  
  61.  
  62.         // Start Regular Update
  63.  
  64.         mHandler = new Handler();
  65.  
  66.         mHandler.post(update);
  67.  
  68.     }
  69.  
  70.    
  71.  
  72.    
  73.  
  74.    
  75.  
  76.     @Override
  77.  
  78.     protected void onPause() {
  79.  
  80.         super.onPause();
  81.  
  82.         mHandler.removeCallbacks(update);
  83.  
  84.     }
  85.  
  86.  
  87.  
  88.     private Runnable update = new Runnable() {
  89.  
  90.         public void run() {
  91.  
  92.             mWorld.update();
  93.  
  94.             mHandler.postDelayed(update, (long) (10/mWorld.timeStep));
  95.  
  96.         }
  97.  
  98.     };
  99.  
  100.    
  101.  
  102.  
  103.  
  104.    
  105.  
  106.     @Override
  107.  
  108.     protected void onResume()
  109.  
  110.     {
  111.  
  112.          super.onResume();
  113.  
  114.     }
  115.  
  116.    
  117.  
  118.     @Override
  119.  
  120.     protected void onStop()
  121.  
  122.     {
  123.  
  124.          super.onStop();
  125.  
  126.     }
  127.  
  128. }
Parsed in 0.041 seconds, using GeSHi 1.0.8.4
MickArea
Junior Developer
Junior Developer
 
Posts: 23
Joined: Mon Feb 23, 2009 4:42 pm

Postby metachris » Fri Mar 06, 2009 2:47 pm

Yeah, i think the error lies here:

MickArea wrote:PhysicsWorld.java
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.     public int targetFPS = 40;
  2.    public float timeStep = 10.0f / targetFPS;
  3.  
Parsed in 0.035 seconds, using GeSHi 1.0.8.4


you set timeStep to 10/targetFPS, which should be 1000/targetFPS!

you could tweak the targetFPS and iterations parameters though...
metachris
Junior Developer
Junior Developer
 
Posts: 24
Joined: Sat Feb 07, 2009 11:40 am

Postby fakeSchwiz » Tue Mar 10, 2009 6:02 pm

I copied and pasted the two classes in the first post, and put them in a new android project, however I am running into problems when the update function in PhysicsWorld is called my app force closes. I am super new to java and the eclipse debugger so im not sure, but I do get an error message that says source code not available if i press jump over enough.
What should I put as the package name to get this to run, or does that even matter? Any other ideas why it keeps crashing?

thanks for your help!
schwiz
fakeSchwiz
Freshman
Freshman
 
Posts: 7
Joined: Tue Mar 10, 2009 5:48 pm

Postby kensuke155 » Thu Mar 12, 2009 12:49 am

Good post. I didn't know Chipmunk was ported to Java. I just did a demo with Phys2d, but it creeps to a halt with about 6 objects as it's not optimized for Java. I would suggest steering clear of Phys2d.
kensuke155
Once Poster
Once Poster
 
Posts: 1
Joined: Sat Sep 20, 2008 10:19 pm

Postby Pinio » Wed Mar 18, 2009 3:39 pm

Hi all,

I think that right now JBox2D isn't a good idea. It generates a lot of garbage, and GC is started almost every second. So, animation is rather poor (even for 3 objects - 1 static and 2 dynamic).

Also Phys2d is rather slow.

Do you know maybe other 2d physic engine, that can be used in Android?

Cheers,
P
Pinio
Freshman
Freshman
 
Posts: 2
Joined: Mon Feb 09, 2009 4:27 pm
Location: PL

Postby MickArea » Wed Mar 18, 2009 5:58 pm

yes i'm interesting too ! so much garbage ...


03-18 17:56:57.816: DEBUG/dalvikvm(753): GC freed 36024 objects / 881392 bytes in 183ms
03-18 17:56:58.766: DEBUG/dalvikvm(753): GC freed 35988 objects / 880752 bytes in 174ms
03-18 17:56:59.746: DEBUG/dalvikvm(753): GC freed 35990 objects / 881272 bytes in 178ms
03-18 17:57:00.786: DEBUG/dalvikvm(753): GC freed 36057 objects / 883232 bytes in 182ms
03-18 17:57:01.776: DEBUG/dalvikvm(753): GC freed 36003 objects / 881200 bytes in 183ms
03-18 17:57:02.796: DEBUG/dalvikvm(753): GC freed 35992 objects / 880584 bytes in 187ms
03-18 17:57:04.556: DEBUG/dalvikvm(753): GC freed 35111 objects / 881192 bytes in 180ms
03-18 17:57:16.186: DEBUG/dalvikvm(753): GC freed 27264 objects / 882384 bytes in 169ms
03-18 17:57:37.826: DEBUG/dalvikvm(753): GC freed 19826 objects / 882504 bytes in 155ms
03-18 17:57:59.366: DEBUG/dalvikvm(753): GC freed 19794 objects / 881104 bytes in 157ms
MickArea
Junior Developer
Junior Developer
 
Posts: 23
Joined: Mon Feb 23, 2009 4:42 pm

Postby lphpc » Fri Mar 20, 2009 5:58 am

I am porting JBox2D testbed to Android. VaryingRestitution test succeed to run now.

http://code.google.com/p/androidbox2d/
lphpc
Freshman
Freshman
 
Posts: 3
Joined: Fri Mar 20, 2009 5:53 am

Top
Next

Return to Novice Tutorials

Who is online

Users browsing this forum: Google [Bot] and 6 guests