processinggame-physicsragdoll

Issues with Ragdoll physics in Processing IDE


I made this account as I have no where else to ask this. So I'm a beginner in coding in general (I have tried python,html and javascript before) and have just started trying out Processing. My goal was to make a rag doll character with draggable arms and legs and realistic falling physics. Now the arms and legs fly out of frame when I start the program and I have no idea how to fix it.

This is the full code I have right now.

float rectWidth = 200, rectHeight = 250; // Body
float faceRectWidth = 150, faceRectHeight = 115; // Face
boolean dragging = false;
PVector target; 
PVector characterPos;
float gravity = 0.5;  // Reduced gravity to prevent limbs from falling too fast
float groundLevel;
boolean isSquinting = false;

float armLength = 60, legLength = 80;
PVector leftArm, rightArm, leftLeg, rightLeg;
PVector leftArmVel, rightArmVel, leftLegVel, rightLegVel;

// Static anchor points for the limbs
PVector leftArmAnchor, rightArmAnchor, leftLegAnchor, rightLegAnchor;

void setup() {
  size(800, 600);
  background(255);
  characterPos = new PVector(width / 2 - rectWidth / 2, height - rectHeight - 30);
  target = new PVector();

  // Set static anchor points relative to the torso position
  leftArmAnchor = new PVector(-20, rectHeight * 0.4);
  rightArmAnchor = new PVector(rectWidth + 20, rectHeight * 0.4);
  leftLegAnchor = new PVector(rectWidth * 0.3, rectHeight);
  rightLegAnchor = new PVector(rectWidth * 0.7, rectHeight);

  // Initialize limb positions at their anchored points
  leftArm = PVector.add(characterPos, leftArmAnchor);
  rightArm = PVector.add(characterPos, rightArmAnchor);
  leftLeg = PVector.add(characterPos, leftLegAnchor);
  rightLeg = PVector.add(characterPos, rightLegAnchor);

  leftArmVel = new PVector(0, 0);
  rightArmVel = new PVector(0, 0);
  leftLegVel = new PVector(0, 0);
  rightLegVel = new PVector(0, 0);

  groundLevel = height - rectHeight;
}

void draw() {
  background(255);

  if (dragging) {
    // Dragging character
    PVector difference = PVector.sub(target, characterPos);
    characterPos.add(difference.mult(0.2));
    target.set(mouseX, mouseY);
    isSquinting = true;
  } else {
    isSquinting = false;
    // Apply gravity
    if (characterPos.y < groundLevel) {
      characterPos.y += gravity;
    } else {
      characterPos.y = groundLevel;
    }
  }

  // Update limb positions, keeping them anchored
  updateLimb(leftArm, leftArmVel, leftArmAnchor);
  updateLimb(rightArm, rightArmVel, rightArmAnchor);
  updateLimb(leftLeg, leftLegVel, leftLegAnchor);
  updateLimb(rightLeg, rightLegVel, rightLegAnchor);

  // Draw character
  pushMatrix();
  translate(characterPos.x, characterPos.y);
  drawBmo();
  popMatrix();
}

void drawBmo() {
  // Draw body
  stroke(#3C4342);
  strokeWeight(5);
  fill(#92D8D2);
  rect(0, 0, rectWidth, rectHeight, 20);  // Rounded corners

  // Draw face
  stroke(#3C4342);
  strokeWeight(5);
  fill(#C9F0EC);
  float faceRectX = (rectWidth - faceRectWidth) / 2;
  float faceRectY = 20; 
  rect(faceRectX, faceRectY, faceRectWidth, faceRectHeight, 20);  // Rounded corners

  // Draw eyes (squinting effect)
  fill(#102C29);
  noStroke();
  float eyeWidth = 10, eyeHeight = isSquinting ? 5 : 10;
  float leftEyeX = faceRectX + faceRectWidth * 0.25;
  float rightEyeX = faceRectX + faceRectWidth * 0.75;
  float eyeY = faceRectY + faceRectHeight * 0.4;
  ellipse(leftEyeX, eyeY, eyeWidth, eyeHeight);
  ellipse(rightEyeX, eyeY, eyeWidth, eyeHeight);

  // Draw mouth
  stroke(#102C29);
  strokeWeight(3);
  noFill();
  float mouthX = faceRectX + faceRectWidth / 2;
  float mouthY = faceRectY + faceRectHeight * 0.6;
  arc(mouthX, mouthY, 35, 10, 0, PI);

  // Draw buttons
  stroke(#102C29);
  strokeWeight(3);
  fill(#072723);
  rect(30, 150, 80, 10);

  fill(#171476);
  ellipse(150, 155, 10, 10);

  fill(#4AE5F5);
  triangle(135, 190, 125, 200, 145, 200);

  // Draw limbs
  drawLimb(leftArm, 10, armLength);
  drawLimb(rightArm, 10, armLength);
  drawLimb(leftLeg, 10, legLength);
  drawLimb(rightLeg, 10, legLength);

  // Other details
  stroke(#102C29);
  strokeWeight(3);
  fill(#2AF53F);
  ellipse(170, 195, 15, 15);
  fill(#F52A82);
  ellipse(150, 225, 25, 25);
}

void drawLimb(PVector pos, float width, float height) {
  // Draw limb as a rectangle
  stroke(#3C4342);
  strokeWeight(3);
  fill(#92D8D2);
  rect(pos.x - characterPos.x, pos.y - characterPos.y, width, height, 10);
}

void updateLimb(PVector pos, PVector vel, PVector anchor) {
  float elasticity = 0.2; // Increased elasticity to keep limbs closer to anchor
  float maxStretch = 50;  // Maximum distance limbs can stretch from the anchor

  PVector anchoredPos = PVector.add(characterPos, anchor);

  // Apply gravity to limbs
  vel.y += gravity * 0.5;  // Reduced gravity effect for limbs
  pos.add(vel);

  // Apply elasticity to keep the limb close to its anchor
  PVector stretch = PVector.sub(anchoredPos, pos);
  float stretchDist = stretch.mag();
  if (stretchDist > maxStretch) {
    stretch.normalize();
    pos.set(PVector.add(anchoredPos, stretch.mult(-maxStretch)));
  }
  pos.add(stretch.mult(elasticity));

  // Apply friction to slow down velocity
  vel.mult(0.95);
}

// Mouse interaction functions
void mousePressed() {
  if (mouseX > characterPos.x && mouseX < characterPos.x + rectWidth &&
      mouseY > characterPos.y && mouseY < characterPos.y + rectHeight) {
    dragging = true;
    target.set(mouseX, mouseY);
  }
}

void mouseDragged() {
  if (dragging) {
    target.set(mouseX, mouseY);
  }
}

void mouseReleased() {
  dragging = false;
}


Solution

  • The following demo creates two classes: body and leg. Both objects may be repositioned by dragging. The leg may be detached and repositioned independently, but always follows the body as it is repositioned. You may be able to use this technique in your application.

    boolean bodySelected = false;
    boolean legSelected = false;
    PVector bodyOffset;
    PVector legOffset;
    
    class Body {
      float x, y, w, h;
    
      Body(float xpos, float ypos, float wide, float ht) {
        x = xpos;
        y = ypos;
        w = wide;
        h = ht;
      }
    
      void display() {
        rect(x, y, w, h);
      }
    }
    
    Body body;
    
    class Leg {
      float x, y, w, h;
    
      Leg(float xpos, float ypos, float wide, float ht) {
        x = xpos;
        y = ypos;
        w = wide;
        h = ht;
      }
    
      void display() {
        rect(x, y, w, h);
      }
    }
    
    Leg leg;
    
    void setup() {
      size(800, 800);
      background(209);
      body = new Body(300, 200, 200, 200);
      bodyOffset = new PVector(0.0, 0.0, 0.0);
      leg = new Leg(body.x + 30, body.y + body.h, 20, 100);
      legOffset = new PVector(0.0, 0.0, 0.0);
    }
    
    void draw() {
      background(209);
      body.display();
      leg.display();
    }
    
    void mousePressed() {
      if ((mouseX >= body.x) && (mouseX <= body.x + body.w) && (mouseY >= body.y) && (mouseY <= body.y + body.h)) {
        bodySelected = true;
        println("mouse pressed x,y = " + mouseX + " : " + mouseY);
        println(body.x + " : " + body.y);
        bodyOffset.set(mouseX - body.x, mouseY - body.y, 0.0);
        // Change offset of leg also
        legOffset.set(mouseX - leg.x, mouseY - leg.y, 0.0);
        println("mouse pressed body offsets = " + bodyOffset.x + " : " + bodyOffset.y);
        println("mouse pressed leg offsets = " + legOffset.x + " : " + legOffset.y);
      } else {
        bodySelected = false;
      }
    
      if ((mouseX >= leg.x) && (mouseX <= leg.x + leg.w) && (mouseY >= leg.y) && (mouseY <= leg.y + leg.h)) {
        legSelected = true;
        println("mouse pressed x,y = " + mouseX + " : " + mouseY);
        println(leg.x + " : " + leg.y);
        legOffset.set(mouseX - leg.x, mouseY - leg.y, 0.0);
        println("mouse pressed offsets = " + legOffset.x + " : " + legOffset.y);
      } else {
        legSelected = false;
      }
    }
    
    void mouseDragged() {
      if (bodySelected) {
        body.x = mouseX - bodyOffset.x;
        body.y = mouseY - bodyOffset.y;
        println("dragged body rect x,y = "+ body.x + " : " + body.y);
        // Keep leg with the body
        leg.x = mouseX - legOffset.x;
        leg.y = mouseY - legOffset.y;
      }
      if (legSelected) {
        leg.x = mouseX - legOffset.x;
        leg.y = mouseY - legOffset.y;
        println("dragged leg rect x,y = " + leg.x + " : " + leg.y);
      }
    }