Talking about Actionscript 3, Box2D and Flash.
From Wikipedia: Wraparound, in video games, is a gameplay variation on the single-screen in which space is finite but unbounded; objects leaving one side of the screen immediately reappear on the opposite side, maintaining speed and trajectory. This is referred to as “wraparound”, since the top and bottom of the screen wrap around to meet, as do the left and right sides (in mathematics, this is known as a Euclidean 2-torus).[1] Some games wrap around in some directions but not others, such as games of the Civilization series that wrap around left to right, or east and west but the top and bottom remain edges representing the North and South Pole (in mathematics, this is known as a cylinder).
In some games such as Asteroids there is no boundary and objects can travel over any part of the screen edge and reappear on the other side. Others such as Pac-Man, Hungry Horace and some games in the Bomberman series, have stages with a boundary surrounding most of the playing area but have few paths connecting the left side to the right, or the top to the bottom, that characters can travel on.
Since I received a couple of emails asking me to achieve a wraparound effect with Box2D, and since the original project which inspired the creation of an educational game with Box2D features it, I am showing you how do it.
It’s very simple, as long as you pay some attention to these three steps:
1) If your world has a floor, make the floor a little bigger than the visible area. In my case, the visible area is 640 pixels wide, while the floor is 800 pixels wide. I don’t want the body wrapping around to hit the side of the floor.
2) If you are moving a body with the mouse, that is you have a mouse joint, disable the effect or the joint will make your body react in a weird way when wrapping around.
3) In all other cases, just update body position using SetPosition method which preserves rotation and speed.
This is the example:
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
public class Main extends Sprite {
private var world:b2World;
private var worldScale:Number=30;
private var mouseJoint:b2MouseJoint;
public function Main() {
world=new b2World(new b2Vec2(0,9.81),true);
debugDraw();
var bodyDef:b2BodyDef=new b2BodyDef();
bodyDef.position.Set(320/worldScale,470/worldScale);
var polygonShape:b2PolygonShape=new b2PolygonShape();
polygonShape.SetAsBox(400/worldScale,10/worldScale);
var fixtureDef:b2FixtureDef=new b2FixtureDef();
fixtureDef.friction=1;
fixtureDef.restitution=0.5;
fixtureDef.shape=polygonShape;
var groundBody:b2Body=world.CreateBody(bodyDef);
groundBody.CreateFixture(fixtureDef);
createBox(Math.random()*500+70,400);
addEventListener(Event.ENTER_FRAME,updateWorld);
stage.addEventListener(MouseEvent.MOUSE_DOWN,createJoint);
}
private function createBox(pX:Number,pY:Number):void {
var bodyDef:b2BodyDef=new b2BodyDef();
bodyDef.position.Set(pX/worldScale,pY/worldScale);
bodyDef.type=b2Body.b2_dynamicBody;
var polygonShape:b2PolygonShape=new b2PolygonShape();
polygonShape.SetAsBox(30/worldScale,30/worldScale);
var fixtureDef:b2FixtureDef=new b2FixtureDef();
fixtureDef.shape=polygonShape;
fixtureDef.density=1;
fixtureDef.friction=0.5;
fixtureDef.restitution=0.2;
var box:b2Body=world.CreateBody(bodyDef);
box.CreateFixture(fixtureDef);
}
private function createJoint(e:MouseEvent):void {
world.QueryPoint(queryCallback,mouseToWorld());
}
private function queryCallback(fixture:b2Fixture):Boolean {
var touchedBody:b2Body=fixture.GetBody();
if (touchedBody.GetType()==b2Body.b2_dynamicBody) {
var jointDef:b2MouseJointDef=new b2MouseJointDef();
jointDef.bodyA=world.GetGroundBody();
jointDef.bodyB=touchedBody;
jointDef.target=mouseToWorld();
jointDef.maxForce=1000*touchedBody.GetMass();
mouseJoint=world.CreateJoint(jointDef) as b2MouseJoint;
stage.addEventListener(MouseEvent.MOUSE_MOVE,moveJoint);
stage.addEventListener(MouseEvent.MOUSE_UP,killJoint);
}
return false;
}
private function moveJoint(e:MouseEvent):void {
mouseJoint.SetTarget(mouseToWorld());
}
private function killJoint(e:MouseEvent):void {
world.DestroyJoint(mouseJoint);
mouseJoint=null;
stage.removeEventListener(MouseEvent.MOUSE_MOVE,moveJoint);
stage.removeEventListener(MouseEvent.MOUSE_UP,killJoint);
}
private function mouseToWorld():b2Vec2 {
return new b2Vec2(mouseX/worldScale,mouseY/worldScale);
}
private function debugDraw():void {
var debugDraw:b2DebugDraw=new b2DebugDraw();
var debugSprite:Sprite=new Sprite();
addChild(debugSprite);
debugDraw.SetSprite(debugSprite);
debugDraw.SetDrawScale(worldScale);
debugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
debugDraw.SetFillAlpha(0.5);
world.SetDebugDraw(debugDraw);
}
private function updateWorld(e:Event):void {
world.Step(1/30,10,10);
world.ClearForces();
for (var b:b2Body=world.GetBodyList(); b; b=b.GetNext()) {
if (b.GetType()==b2Body.b2_dynamicBody) {
if (b.GetJointList()==null) {
if (b.GetPosition().x*worldScale>640) {
b.SetPosition(new b2Vec2(0,b.GetPosition().y));
}
if (b.GetPosition().x*worldScale<0) {
b.SetPosition(new b2Vec2(640/worldScale,b.GetPosition().y));
}
}
}
}
world.DrawDebugData();
}
}
}
And this is the result:
Pick the box with the mouse and throw it off the screen.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.