World Construction Kit

jesses edited this page Sep 27, 2010 · 25 revisions

World Construction Kit (WCK) is a component framework for the Box2D Flash Alchemy Port, which allows 2D physics simulations for games / websites to be authored entirely within the Flash IDE.

As WCK Components (Worlds, BodyShapes and Joints) are added to the stage, either via code or the timeline, they automatically create the corresponding Box2d physics object. When removed from the stage, the Box2D object is destroyed. Transformations (rotation, scale and skew), and deeply nested instances in the display hierarchy, all translate correctly into the physics simulation.

Getting Started

The demo project file is included with the source. Playing around with the demo is good way to get acquainted with WCK’s features. There is also a blank project provided with the necessary libraries already set up correctly.

If you are using Flash CS4, make sure you have at least version 10.0.2. Earlier version have problems with large projects and will not run the demo project.

Starting a New Project

Note: This guide assumes a basic knowledge of Flash and AS3 for the sake of brevity. If you want a more in-dept tutorial try the step-by-step quick start guide.

You will need to include the Box2DAS/Box2D.swc file and the WCK source in your project. Under ActionScript 3.0 Settings:

  • Include the folder containing the entire WCK source code under the Source Path tab.1
  • Add the Box2DAS/Box2D.swc file under the Library Path tab.
  • Use wck.WCK for the Document Class2

Also check Export SWC under Publish Settings.3

1 If downloaded from github, pick the downloaded folder containing everything (it should contain Box2DAS, wck, etc.).

2 Or use a Document Class that extends wck.WCK. The important thing is that b2Base.initialize(); and Input.initialize(stage); are called.

3 You do not need the generated SWC file, but Flash will not compile the project without this checked.

Creating a Simple World and Box.

Create a new symbol with a base class of wck.World and drag it onto the stage. This will be the physics world that contains your physics entities.

Create a new symbol with a base class of shapes.Box. Open it and draw a box (approximately 100 × 100 pixels) centered on the stage. The shape.Box class should automatically read the dimensions of your box, but it requires the shape to be centered.

Now drop an instance of your box into the world symbol.

Debug your project. You should get a box that simply falls off the bottom of the stage due to gravity. If you get an error, or the simulation does not run, make sure that your box is inside your world and that your world is on the stage. Also make sure the necessary libraries are being included.

Setting Up Component Properties For The Box and World Symbols

Right click the box symbol in your library and open Component Definition. Enter the class as wck.BodyShape. Do the same for your world symbol, but use the class wck.World. You should now be able to edit the properties of your world and box in the component inspector panel.

Drop a new box symbol into your world. Stretch it out so it forms a floor underneath the existing box. In the component inspector panel, change the type to Static. This tells the engine that your stretched box should not move.

Debug the project again. Your falling box should now hit the static floor and stop.

Creating a Simple Revolute Joint

Create a new symbol with a base class of wck.Joint. Draw a tiny circle to serve as a visual representation of the joint. Set the component definition class of this symbol to wck.Joint.

Drop this joint symbol into your world so that it overlaps one corner of the first box you created. Make sure the center of the joint is overlapping part of the box; the Joint class looks for objects underneath it’s center.

Change the type property to Revolute. Debug the project. Your dynamic box should now be anchored to wherever you placed your joint.

Creating a Simple Distance Joint

Drag another box into the world. Give it an instance name of “myBox”.

Select your previously created joint, and in the component inspector change the type to Distance. Also change the target2Name to “myBox”. This tells the distance joint to use “myBox” as the other body involved in the joint.

Debug your project. The two dynamic boxes should be constrained by the distance joint.

Create a Simple Body Containing Multiple Shapes

Create a new symbol with a base class of wck.BodyShape. Within this symbol place two boxes so that they overlap, but are not completely on top of each other. Try rotating, scaling, or skewing them. What you are creating is a body (the symbol with base wck.BodyShape) that contains two shapes (the two transformed boxes). When done, drag your new body symbol into your world.

Drag another instance of this body symbol into your world and try scaling, skewing, and rotating it so it looks drastically different than your first body instance.

Debug your project. The shapes within each body symbol should move as if they are one body. The transforms (skew, scale and rotation) you applied to your symbols should also correctly translate into the physics simulation.

Other Things to Try

  • Changing the other component properties of your boxes.
  • Other joint types.
  • Changing the component properties of the world.

Using The wck.World Class

wck.World represents a Box2DAS.Dynamics.b2World object. The b2World instance can be accessed via the b2world property. wck.World can be used as a base class for library symbols and extended by your own classes to add custom behavior to worlds. All physics entities (instances of wck.BodyShape and wck.Joint) must be children of a wck.World instance.

Note: Flash CS5 includes an undocumented class named World that may create a namespace conflict in your code. Referencing the World class as the fully qualified class name fixes this:

myWorld = new wck.World();

Using The BodyShape Class

wck.BodyShape represents either a Box2DAS.Dynamics.b2Body object, a Box2DAS.Dynamics.b2Fixture object or both, depending on the context it is used. A BodyShape defines physics shapes. If placed within a World, those shapes will define an independent body. If placed within another BodyShape, those shapes will be appended to that body. If a transform (scale, skew, rotation) is applied to the BodyShape, the Box2D shapes it defines will also be transformed appropriately.

There are several ways to define the shape(s) of your library symbols:

Defining Shapes By Extending The shapes.* Classes

To create a simple box shape, create a symbol with a base class of shapes.Box and draw within it a box centered on the origin. Specify a base class of shapes.Oval and draw a circle to define a (stretchable) circle. The base classes in shapes.* use the dimensions (width & height) of the symbol to automatically determine the width and height of the shape.

Defining Shapes By Adding Child Shapes

Defining shapes by extending shapes.* classes is efficient, but presents a problem; What if the visual dimensions of a symbol doesn’t match its physical dimensions? Example scenario: A box shaped character with extended “arms” that should not influence the size of the core box shape:

>----[^^]----<

In this case a “guide” shape symbol extending shapes.Box can be created and placed as a child within the box-with-arms symbol. The box-with-arms symbol should specify a base class of wck.BodyShape and not define any shapes of it’s own. In this setup, box-with-arms creates and represents a b2Body and the guide shape creates and represents a b2Fixture because it is a child of another BodyShape.

If you want the guide / child shape to use the component properties of its parent, set the isGuideShape property on the child to true. Using the box-with-arms example, setting isGuideShape on the child box object will allow you to have box-with-arms instances with different restitution, density, and other shape-specific properties. These properties would normally be locked to the value set within the box-with-arms symbol on the child.

Defining Shapes By Implementing The shapes() Function

BodyShape can be extended to add behavior and define shapes for your objects. The shapes() function can be overridden to manually create shapes. Example:

public class Box extends BodyShape {
	public override function shapes():void {
		box(20, 20); // Box centered on origin.
		circle(10, new V2(0, 20)); // Circle positioned on top of box.
	}
} 

Defining Shapes On The Timeline

Timeline code executes before BodyShape has a chance to initialize the Box2D environment. The shape() function will queue calls to shape definition functions from the timeline, then call those functions later once the b2Body is ready. Example:

shape(box, 20, 20); // Box centered on origin.
shape(circle, 10, new V2(0, 20)); // Circle positioned on top of box.

Defining shapes this way avoids littering a project with tiny classes that do nothing besides define shapes. Of course more complex objects (actors, etc.) should always have their own classes defined.

The Shape Definition Functions

All shape definition functions, when possible, use the transformation of the BodyShape to define the size of the shape If no size / position data is passed in explicitly.

box(w:Number = 0, h:Number = 0, pos:V2 = null, angle:Number = 0);

Defines a box. Width, height, position and angle.

circle(radius:Number = 0, pos:V2 = null);

Defines a circle. Radius and position.

oval(width:Number = 0, height:Number = 0, pos:V2 = null, sides:uint = 0, detail:Number = 4, angle:Number = 0);

Defines a “stretchable” circle. If the height and width are the same, a regular circle shape is used. Otherwise a polygon approximation is created. sides and detail can be used to control the detail of the polygon.

polyN(sides:uint = 5, radius:Number = 0, pos:V2 = null, angle:Number = 0);

Defines a N-sided equilateral polygon. Number of sides, radius (width), position and angle.

arc(degrees:Number = 360, sides:uint = 0, radius:Number = 0, pos:V2 = null, angle:Number = 0, detail:Number = 4);

Defines a polygon approximation of a semi-circle. degrees indicates how much of the circle to build.

lineArc(degrees:Number = 360, sides:uint = 0, radius:Number = 0, pos:V2 = null, angle:Number = 0, detail:Number = 8);

Like arc() but creates the semi circle using lines, so the inside of the arc is hollow. Handy for creating concave arcs.

triangle(w:Number = 0, h:Number = 0, pos:V2 = null, angle:Number = 0);

A right triangle, or a box with the top right corner missing.

line(v1:V2 = null, v2:V2 = null);

A line from one point to another.

poly(vertices:Array);

One polygon. The vertices should be passed in the format [[x1, y1], [x2, y2], [x3, y3]...].

polys(vertices:Array);

Multiply polygons. The output from the polygon decomposition JSFL script can be passed directly to this function.

decomposedPoly(vertices:Vector.<Number>);

A polygon that will be decomposed at runtime. Expects vertices as a flat number vector in the format [x1, y1, x2, y2, x3, y3...]

polygon(vertices:Vector.<V2>)

A single polygon defined as a vector of V2 points.

Using The Joint Class

Docs coming soon.