Friday, July 27, 2012

Drawing Line in the Box2D world

I wanted to draw a line in the Box2D world using the mouse click and Drag. This would be helpful in games where I need to Dynamically draw a line to cut a joint or body.

Step 1: Add EventListeners for the mouse events
document.addEventListener("mousedown", handleMouseDown, true);
document.addEventListener("mouseup", handleMouseUp, true);
document.addEventListener("mousemove", handleMouseMove, true);
Step 2: Event Handlers
//Start Drag when the mouse click happens and save the start position
var mouseX, mouseY, dragMode = false, line = null;
function handleMouseDown(e) { 
    mouseX = (e.clientX - canvas.offsetLeft) / SCALE;
    mouseY = (e.clientY - canvas.offsetTop) / SCALE;
    dragMode = true;
}
function handleMouseUp(e) { 
 dragMode = false;
 currentMouseX = (e.clientX - canvas.offsetLeft) / SCALE;
 currentMouseY = (e.clientY - canvas.offsetTop) / SCALE; 
 var start = new b2Vec2(mouseX, mouseY);
 var end = new b2Vec2(currentMouseX, currentMouseY);
 line = drawline(line, start, end);
 
}
function handleMouseMove(e) {
 if(dragMode)
 {
  currentMouseX = (e.clientX - canvas.offsetLeft) / SCALE;
  currentMouseY = (e.clientY - canvas.offsetTop) / SCALE; 
  var start = new b2Vec2(mouseX, mouseY);
  var end = new b2Vec2(currentMouseX, currentMouseY);
  line = drawline(line, start, end);  
 }
}
function keyset(evt){
}
Step 3: updating the to redraw the new line
function update() { 
    ....
    var lineCount = 0;
    for (b = world.GetBodyList() ; b; b = b.GetNext()) { 
       if(b.GetUserData() != null){
           if( b.GetUserData().name == "Line"){
               updateDrawLine(b);
               lineCount++;
            if(dragMode && lineCount > 1) objScheduledForRemoval[++index] = b;
        }
    ....

In the above code snippet, I am drawing a new line and deleting the old one as my mouse gets dragged. The issue I faced while doing this is if I hover over the same point without moving then the line disappears to fix this I have added the lineCount parameter, to make sure atleast one line is visible to the user at all times during the drag. On a side note, Box2D doesn't like it when you attempt to remove a body from the world when the world is being stepped. Hence I am adding the objects to be removed to a list and removing them later.

//------------------------------- Removing objects from the world
window.setInterval(removeObjScheduledForRemoval, 1000/90);
var objScheduledForRemoval = Array();
var index = -1;
function removeObjScheduledForRemoval()
{
 for(var i = 0; i <= index; i++){
  console.log("here",i);
  world.DestroyBody(objScheduledForRemoval[i]); 
  objScheduledForRemoval[i] = null; 
 }
 objScheduledForRemoval = Array();
 index = -1;
}

Step 4: Drawing the line in the Box2D world.

function drawline(b,start,end) {
    var fixDef = new b2FixtureDef;
  fixDef.shape = new b2PolygonShape;
  fixDef.density = 1.0;
  fixDef.friction = 0.5;
  fixDef.restitution = .5;
  fixDef.shape.SetAsArray([
       start,
       end],2
  );
  var bodyDef = new b2BodyDef;
  bodyDef.type = b2Body.b2_staticBody;
  bodyDef.position.Set(0,0);
  rbData = new Object();
  rbData.name = "Line";
  rbData.start = start;
  rbData.end = end;
  bodyDef.userData = rbData;
  var line = world.CreateBody(bodyDef).CreateFixture(fixDef);
  return line;
}
Step 4: Mapping the line to the canvas.
function  updateDrawLine(b){
 start = b.GetUserData().start;
 end = b.GetUserData().end;
 context.save(); 
 context.beginPath();
 context.moveTo(start.x * SCALE,start.y * SCALE);
 context.lineTo(end.x * SCALE,end.y *SCALE);
 context.stroke();
 context.restore();
}

No comments:

Post a Comment