WORLD CAMERA

28
Jun
2012

Here we'll learn how to follow a Box2Dweb object with the camera. Box2D does not have a "camera", so we will have to move the stage according to body position. I have used a little bit of css and translate function associated with "canvas".

1) We will create a canvas element inside a div.

Here the outer div with id cover will act as our viewport with dimensions 600px x 420px. The canvas has a width of 2000px.

2) Here the body will move only in x direction so this method will work only to keep the body in the center of the screen in x direction. But the same can be done for y direction easily.
What are we going to do is we will calculate the distance (read pixels) moved by the object (to be followed by the camera) in each time step and we will move the canvas in opposite direction by that very distance. Here is the code snippet for it.
var cw = $('#cover').width(); // Viewport Width
var dw = $('#canvas').width(); // Drawing Width
var p2 = (car.GetWorldCenter().x)*30; // Initial Position of Car -- X-Coordinate. Multiplied by 30 to convert it into pixels.
var halfwidth = (cw/2);

// START -- Initial Setting of camera
if((p2>halfwidth) && (dw-p2)>halfwidth)
{
	context.translate(-(p2-halfwidth),0);
}
else if((dw-p2) < halfwidth)	{
	context.translate(-(dw-cw),0);
}
// END -- Initial Setting of camera

var pos = new Array();
pos[0] = car.GetWorldCenter().x * 30;

function update() {
	world.Step(1 / 60, 10, 10);
	world.DrawDebugData();
	world.ClearForces();
	
    // USED for Steering of car
    if(steerforward == true)	{
		revoluteJointA.SetMotorSpeed(330);
		revoluteJointB.SetMotorSpeed(330);
	}
	if(steerforward == false && steerbackward == false)	{
		revoluteJointA.SetMotorSpeed(0);
		revoluteJointB.SetMotorSpeed(0);
	}
	if(steerbackward == true)	{
		revoluteJointA.SetMotorSpeed(-330);
		revoluteJointB.SetMotorSpeed(-330);
	}

	// START -- MOVE CAMERA
	var p = new b2Vec2();
	p = (car.GetWorldCenter().x)*30;
	pos.push(p);
	var length = pos.length;
		
	var s = (pos[length-1]-pos[length-2]); //in pixels
			
	if((halfwidth < (dw-p)) && ( p > halfwidth))
	{
		context.translate(-s,0);
	}
    // END -- MOVE CAMERA
};
3) Let's see in Detail. Lines 6 - 14 will set the camera so as to center the object in the middle of the screen if it is possible INITIALLY. It is not possible in the following situations:



If the object were in the shaded black region, it will not be possible to center the object in the middle of the screen.

Line 7 checks whether the object is in between the two regions. If it is then the object can be centered by the translating the canvas by (p2 - halfwidth) pixels where p2 is the distance from left of the canvas in pixels.



Line 11 - 13 --> But if the object were in the extreme right shaded black region if can't be centered. But we have to show the object, so we'll translate the object by (dw-cw) pixels.



4) Lines 38 - 50 deals with movement of camera when the object moves. In line 16, I have created a new Array to store the position of the object after every timestep. Using this array we'll be able to find to out by what distance does the object moves after each time step. We can get this by finding the difference between the two latest entries in the array. Then we will move the canvas by the same difference in opposite direction.

Line 17 --> Initial position of the object is stored.

Line 39, 40, 41 --> A new Vector is defined which will store the current X-Coordinate of the object.

Line 42 --> Length of the array storing the position is stored so that using this as index, the two latest entries at index (length - 1) & (length - 2) can be retrieved.

Line 44 --> Finds out the distance moved in one timestep. It is multiplied by 30 to convert meters to pixels (30 was the scale used in debugDraw). It is converted to pixels as translate() takes its arguements in pixels.

Line 46 --> Checks that the object is not in the shaded black region.

Line 48 --> Canvas is translated.


(Use Left and Right to Move. 'R' for reset)

By administrator at 02:28:28 PM 11 Comment(s)

Comments

Hey nice example. What if we want to add images init how would I can add image on dynamic and static body.
By Rahul on 21 Oct, 2012 at 12:56:01 PM
Using images in place of boring bodies. Is that what you are asking?
By administrator on 21 Oct, 2012 at 04:12:43 PM
Check the latest article on Attaching Images. I hope that's what you're looking for.
By administrator on 22 Oct, 2012 at 12:51:01 AM

nice tutorial - worked perfectly. Would it technically be better to update the x position of the world within box2d on the update function rather than relying on actually moving the canvas element itself?

By al on 13 Dec, 2012 at 01:37:21 PM

Yes it will be. Actually I tried that way initially but couldn't make it work. So I tried it this way, that is by creating a very wide canvas and inside a smaller div tag and setting its overflow property to hidden. If I do it in this way then translating the canvas is the only viable option.

By administrator on 14 Dec, 2012 at 01:32:51 PM

In lines 6 to 14 you say that (if possible) it centers the car only initialy. But which part of code centers it afterwards?
Meaning, which part of code is restraining the car from leaving the sight?
I cannot understand what am I missing from making it work…

By haris on 9 Aug, 2014 at 10:36:36 AM

@haris lines 38 to 50 restrains the car from leaving the sight. If the car is between the shaded region (see figure) the car will be in the center of the screen.

By administrator on 9 Aug, 2014 at 05:36:07 PM

That's what i also thought. But for a reason the .translate() function
is not responding. Shouldn't i also type things like .clearRect() or .save()
functions?
Or actually, the problem might be in the s = (pos[length-1]-pos[length-2]) variable.
It is always zero!!! Even when the car is actually moving because (and leaving the view
because something isn't working). Do you have any suggestions for this?

By haris on 11 Aug, 2014 at 12:43:04 PM

I hope that I'm not spamming, but the problem lies in the pos array.
It will only take 2 parameters, 2 spots, which are both equal to the initial
position of the car, and thus results in s being equal to zero.
Can you tell me why it takes only 2 parameters?

By haris on 11 Aug, 2014 at 01:05:57 PM

@harris they aren't same! pos[0] takes the initial position while

var p = new b2Vec2();
p = (car.GetWorldCenter().x)*30;
pos.push(p);

the above code which is in the update() takes the value in the next step and pushes it in the array. So pos[length-2] will have initial position and pos[length-1] will have the new position.

By administrator on 11 Aug, 2014 at 06:04:28 PM

Lot's of thanks for your clarification. Realy helped!

By harris on 12 Aug, 2014 at 09:41:01 PM

Add a Comment

Please enter the email address.Invalid format. (Won't be Displayed)
Notify me of followup comments
Enter the displayed Code: captcha