Box2D for Flash Games demos in LibGDX with Projects for Desktop, Android, and HTML5

banner

I recently reviewed Emanuele Feronato’s new book “Box2D for Flash Games”, and created all the demos from the book in FlashDevelop.
In this post, I wanted to bring them all into LibGDX, so that everyone could also see how to work in Box2D using LibGDX, to develop Android, Desktop, and HTML5 Box2D games.

I would recommend picking up his book if you are interested in Box2D, and want to understand these demos from his book better. His site is also one of the best for tutorials an examples for beginner and intermediate game developers. (http://www.emanueleferonato.com/2012/11/28/box2d-for-flash-games-book-is-on-the-shelves/)

Obviously the syntax between AS3 and Java are a little different, but most of the lines of code worked pretty well, only having to change variable declarations, of function capitalization.

The only large problem I ran into was the implementation of JBox2d on LibGDX for the HTML5 version of the project. Everything still built correctly, and works, but blocks begin randomly shifting and falling over, and I wasn’t able to discover a solution to this problem yet.

In Example 2.1, we build on chapter one by adding objects to our world, making them dynamic, and changing the material properties, to allow the objects to interact differently.


Download the source code for the eclipse projects for Desktop, Android, and HTML5

And so you can see how easy it is to setup a basic Box2D world in LibGDX, that compiles for Desktop, Android and HTML5:
(The main difference is that OpenGL/LibGDX use the bottom-left corner as 0,0 (normal Cartesian) , whereas Flash uses the top left as 0,0, with down increasing in y-value, so for any y-coordinates from AS3, you have to do SCREEN_HEIGHT-y_coord. Also, I keep track of screen scaling, to scale everything relative to screen size, since obviously most Android devices can’t display 640×480 resolution, and should be full screen, regardless)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package com.chrismweb.box2dforflashgames;
 
 
import java.util.Iterator;
 
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
 
 
/**
 * ...
 * #Chp2.1 - Ball drop with bounce
 * @book by Emanuele Feronato
 * http://www.emanueleferonato.com/
 * Get it now @ http://www.packtpub.com/box2d-for-flash-games/book
 */
 
public class MyGdxGame implements ApplicationListener
{
	//for drawing
	private OrthographicCamera camera;
	private SpriteBatch batch;
 
	//
	Vector3 touchPoint;
 
 
 
	public static float SCREEN_WIDTH_ORIGINAL=640;
	public static float SCREEN_HEIGHT_ORIGINAL=480;
 
	public static float SCREEN_WIDTH=640;
	public static float SCREEN_HEIGHT=480;
 
	//for testing other screen resolutions quickly in the desktop version
//	public static float SCREEN_WIDTH=1280;
//	public static float SCREEN_HEIGHT=800;
 
	public static float BOX2D_WIDTH_SCALE=1;
	public static float BOX2D_HEIGHT_SCALE=1;
 
	//box2d variables
	private World world;
	private Box2DDebugRenderer debugRenderer;
	public static float PIXELS_PER_METER = 30.0f;
 
	//reset variables
	BitmapFont font;
	Rectangle rect_reset;
	private ShapeRenderer renderer;
 
 
 
	@Override
	public void create()
	{
		SCREEN_WIDTH= Gdx.graphics.getWidth();
		SCREEN_HEIGHT= Gdx.graphics.getHeight();
 
		//NOTE: with different screen sizes (on android for instance) the world will be setup as different sizes, so the mass of objects won't be the same.
		//you need to create the world the same size, and scale the camera, so that it behaves the same regardless of screen resolution
		BOX2D_WIDTH_SCALE=SCREEN_WIDTH_ORIGINAL/SCREEN_WIDTH;
		BOX2D_HEIGHT_SCALE=SCREEN_HEIGHT_ORIGINAL/SCREEN_HEIGHT;
 
 
		// setup the camera. In Box2D we operate on a
		// meter scale, pixels won't do it. So we use
		// an orthographic camera with a viewport of
		// 48 meters in width and 32 meters in height.
		// We also position the camera so that it
		// looks at (0,16) (that's where the middle of the
		// screen will be located).
		camera = new OrthographicCamera(PixToMeter(SCREEN_WIDTH)*BOX2D_WIDTH_SCALE, PixToMeter(SCREEN_HEIGHT)*BOX2D_HEIGHT_SCALE);
		camera.position.set(PixToMeter(SCREEN_WIDTH)/2*BOX2D_WIDTH_SCALE, PixToMeter(SCREEN_HEIGHT)/2*BOX2D_HEIGHT_SCALE, 0);
 
		batch = new SpriteBatch();
 
		touchPoint = new Vector3(0,0,0);
 
		Vector2 gravity=new Vector2(0.0f, -9.81f); //we have to flip ALL y values- AS3 uses down int he y duirection as increasing, whereas OpenGL uses UP in the y direction as positive
		Boolean sleep = true;
		world = new World(gravity, sleep);
		debugRenderer = new Box2DDebugRenderer();
 
		Setup();
 
		font = new BitmapFont(Gdx.files.internal("resources/fonts/arial-15.fnt"), false);
		font.setScale(1/BOX2D_HEIGHT_SCALE);
 
		rect_reset = new Rectangle(0, SCREEN_HEIGHT-30/BOX2D_HEIGHT_SCALE, 100/BOX2D_WIDTH_SCALE, 30/BOX2D_HEIGHT_SCALE);
		renderer = new ShapeRenderer();
 
		Gdx.app.log("MyGdxGame", "GAME STARTED");
 
	}
 
	private void ResetWorld()
	{
		Gdx.app.log("MyGdxGame", "Resetting world");
		Iterator<Body> iterator = world.getBodies();
		while(iterator.hasNext()) 
		{
	         Body body = iterator.next();
	         world.destroyBody(body);
		}
		Setup();
	}
 
	public void Setup()
	{
		//for creating a circle
		//first create a bodydef
		BodyDef bodyDef = new BodyDef();
		bodyDef.position.set(PixToMeter(SCREEN_WIDTH_ORIGINAL/2), PixToMeter(SCREEN_HEIGHT_ORIGINAL-30));
		bodyDef.type = BodyType.DynamicBody;
 
		CircleShape circleShape = new CircleShape();
		circleShape.setRadius(PixToMeter(25));
 
		FixtureDef fixtureDef = new FixtureDef();
		fixtureDef.shape=circleShape;
		fixtureDef.density=1;
		fixtureDef.restitution=0.6f;
		fixtureDef.friction=0.1f;
		Body theBall = world.createBody(bodyDef);
		theBall.createFixture(fixtureDef);
 
		//for creating the "floor" bottom rectangle
		bodyDef.type=BodyType.StaticBody;
		bodyDef.position.set(PixToMeter(SCREEN_WIDTH_ORIGINAL/2), PixToMeter(SCREEN_HEIGHT_ORIGINAL-470));//center location of the box- 320 to the right (640/2), and 10px from the bottom(480)
		PolygonShape polygonShape=new PolygonShape();
		polygonShape.setAsBox(PixToMeter(SCREEN_WIDTH_ORIGINAL/2), PixToMeter(10));//1/2 width and height, so 640x20 px box
		fixtureDef.shape=polygonShape;
		Body theFloor=world.createBody(bodyDef);
		theFloor.createFixture(fixtureDef);
	}
 
	private float PixToMeter(float pixels)
	{
		return pixels / PIXELS_PER_METER;
	}
	private float MeterToPix(float meters)
	{
		return meters * PIXELS_PER_METER;
	}
 
	@Override
	public void dispose()
	{
		batch.dispose();
 
	}
 
	@Override
	public void render()
	{
		world.step(Gdx.app.getGraphics().getDeltaTime(), 3, 3);
 
		Gdx.gl.glClearColor(.2f, .2f, .2f, 1); //0->1 = 0->255
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		camera.update();
 
 
 
		debugRenderer.render(world, camera.combined);
 
 
 
//		renderer.begin(type)
		renderer.setColor(.05f, .2f, .31f, 1.0f);
		renderer.begin(ShapeType.FilledRectangle);
		renderer.filledRect(rect_reset.x, rect_reset.y, rect_reset.width, rect_reset.height);
		renderer.end();
 
		batch.begin();
		font.drawMultiLine(batch, "Reset", rect_reset.x, rect_reset.y+font.getLineHeight(), rect_reset.width, HAlignment.CENTER);
		batch.end();
 
		Update();
	}
	public void Update()
	{
		// translate the mouse coordinates to world coordinates
		if (Gdx.input.justTouched()) 
		{
			camera.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
			Vector2 touchPointPixels =new Vector2(MeterToPix(touchPoint.x)/BOX2D_WIDTH_SCALE, MeterToPix(touchPoint.y)/BOX2D_HEIGHT_SCALE);
 
			Gdx.app.log("MyGdxGame", "GAME touchPointPixels: "+touchPointPixels);
			if(rect_reset.contains(touchPointPixels.x, touchPointPixels.y))
				ResetWorld();
		}
	}
 
	@Override
	public void resize(int width, int height)
	{
	}
 
	@Override
	public void pause()
	{
	}
 
	@Override
	public void resume()
	{
	}
}

After that, in Example 2.2 we also create a “Totem” game level, and create the totem object using compound objects to make a complex object from simple shapes.
We also create the head of the totem as a generic polygon, by specifying individual vertices to create the shape. (Blocks for some reason behave somewhat like jelly in the HTML5 with LibGDX and JBox2d)


Download the source code for the eclipse projects for Desktop, Android, and HTML5

In Example 3.1, we then learn how to convert mouse input to box2D meters, how to delete a body, and how to selectively get physical information about certain bodies that we are interested in.
(click on certain bodies to destroy them)


Download the source code for the eclipse projects for Desktop, Android, and HTML5

I will upload the rest of the demos and source code for each in the near future.

Add A Comment

Subscribe

Archives

Find online and local Game Development Classes