• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

SOLVED Rope swing without physics engine

ErlandSol

Member
Im trying to implement a rope swing into my game, i have a bit of experience with GML, but im still learning

I dont want to use the built in physics engine, instead i have made my own system based in physics
Its quite primitive, and a little messy, but it works, and i understand it, and im thinking about refining it a bit.

I have followed some tutorials and forum posts, and have managed to get a functioning rope swing, but it doesnt function
the way i want it to..
That one is based on creating an angular force using dcos() and dsin(), then into angular accelleration and angular speed,
which splits up into x and y speeds using lengthdir_x() and lengthdir_y()


GML:
// "Shoot" rope
if (mouse_check_button_pressed(mb_left)) {
    xHook = mouse_x;
    yHook = mouse_y;// - oTree.scroll;
    xRope = x;
    yRope = y;       
    ropeLength = point_distance(xHook,yHook,x,y);
    ropeAngle = point_direction(xHook,yHook,x,y);
    angSpeed = 0;   
}

// Calc x and y forces
xDrag = -0.4*dcos(ropeAngle-90)*angSpeed;
yDrag = -0.4*dsin(ropeAngle-90)*angSpeed;
xForce =  xDrag;
yForce =  gameValues.grav + yDrag;


// Calc. angular force -> speed
angForce = xForce*dcos(ropeAngle-90) + yForce*dsin(ropeAngle-90);
angAcc = angForce/mass;
angSpeed += angAcc; 
ropeAngle += angSpeed;
    
    
// Calc. x and y speed   
xRope = xHook - lengthdir_x(ropeLength, ropeAngle-180);
yRope = yHook - lengthdir_y(ropeLength, ropeAngle-180);
xSpeed = xRope - x;
ySpeed = yRope - y;
    
    
// Move   
x += xSpeed;
y += ySpeed;


The problem is that the character is locked to a "circle", so even if the character is straight above the "anchor" of the rope, it will follow the circle all the way around downwards

Im looking for a way to make the character move freely, unless the rope is "tight", so it is constricted by, but not locked to the rope, if that makes sense.


phys3.png


I could probably make it work with a lot of if statements and headaches, but im looking for something more elegant and stable

Thanks in advance! šŸ˜
 

rytan451

Member
I've done some math and physics and come up with some code. In short: it calculates gravity and drag, then it attempts to move the swing. If the swing will stretch the rope (so if the position of the object is further from the rope anchor position than a set amount), it calculates how much force the rope needs to exert towards the rope anchor point, so that it will bounce by a set amount. (In my code, the amount is set to 0; it should be set between 0 and 1 inclusive, but values greater than 1 are possible in the simulation, if not possible in real life.) It applies that force (remember: the swing has unit mass), and finally, it moves the object.

Caution: this code is untested, and may not work as expected.

GML:
// Create

ropeAnchorX = 100;
ropeAnchorY = 100;
ropeLength = 10;
velX = 0.0;
velY = 0.0;
grav = 0.5;
drag = 0.01;
restitution = 0.0;

// Step

velY += grav;
velX *= 1.0 - drag;
velY *= 1.0 - drag;

var nx, ny, nmag, invm, forceMag;
nx = x + velX - ropeAnchorX;
ny = y + velY - ropeAnchorY;

if (sqr(nx) + sqr(ny) > sqr(ropeLength)) {
  nmag = sqrt(sqr(nx) + sqr(ny))
  invm = 1.0/nmag;
  nx *= invm;
  ny *= invm;
  
  forceMag = dot_product(nx, ny, velX, velY);
  if (forceMag > 0.0) {
    velX -= forceMag * nx * (1.0 + restitution);
    velY -= forceMag * ny * (1.0 + restitution);
  }
  
  nx = x + velX - ropeAnchorX;
  ny = y + velY - ropeAnchorY;
}

x = nx + ropeAnchorX;
y = ny + ropeAnchorY;
 

ErlandSol

Member
I've done some math and physics and come up with some code. In short: it calculates gravity and drag, then it attempts to move the swing. If the swing will stretch the rope (so if the position of the object is further from the rope anchor position than a set amount), it calculates how much force the rope needs to exert towards the rope anchor point, so that it will bounce by a set amount. (In my code, the amount is set to 0; it should be set between 0 and 1 inclusive, but values greater than 1 are possible in the simulation, if not possible in real life.) It applies that force (remember: the swing has unit mass), and finally, it moves the object.

Caution: this code is untested, and may not work as expected.

GML:
// Create

ropeAnchorX = 100;
ropeAnchorY = 100;
ropeLength = 10;
velX = 0.0;
velY = 0.0;
grav = 0.5;
drag = 0.01;
restitution = 0.0;

// Step

velY += grav;
velX *= 1.0 - drag;
velY *= 1.0 - drag;

var nx, ny, nmag, invm, forceMag;
nx = x + velX - ropeAnchorX;
ny = y + velY - ropeAnchorY;

if (sqr(nx) + sqr(ny) > sqr(ropeLength)) {
  nmag = sqrt(sqr(nx) + sqr(ny))
  invm = 1.0/nmag;
  nx *= invm;
  ny *= invm;
 
  forceMag = dot_product(nx, ny, velX, velY);
  if (forceMag > 0.0) {
    velX -= forceMag * nx * (1.0 + restitution);
    velY -= forceMag * ny * (1.0 + restitution);
  }
 
  nx = x + velX - ropeAnchorX;
  ny = y + velY - ropeAnchorY;
}

x = nx + ropeAnchorX;
y = ny + ropeAnchorY;
It works perfectly! Thank you so much! šŸ˜ Now its time to analyze and understand it all, i usually dont like copy-pasting stuff, but as long as i can learn from it, and understand enough to tweak it to fit my game, im okay with it
 

rytan451

Member
Wait what? (Code never works on the first try.)

Anyways, if there's anything you're struggling to figure out, feel free to ask in this thread, or PM me. I'll note that the part of the program that's probably most confusing is the dot product between the normalized vector pointing from the anchor to the object and the velocity vector. Here's what it's trying to figure out: how fast is the object going in the direction away from the center?

After figuring it out, I find a vector to add to the velocity such that the object will no longer be going away from the center. This is multiplied by 1.0 + restitution, so that it "bounces". With a restitution of 0, it does not bounce at all. With a restitution of 1, it bounces such that the speed before and after the bounce is equal.

I've also noticed that there may be a minor bug: it's possible that the object may stray further from the rope anchor point than the rope would permit. Just add a bit of a spring effect to the rope (apply an inwards force proportional to how far beyond the rope length the object is) and that should fix the problem. I'll add this to the original code.

Finally, I'm glad that you're taking the time to analyze and understand the code. You can only improve if you aren't mindlessly copying!

To the end of helping you understand the code, I've added annotations in the form of comments. I hope that this assists your understanding!

GML:
// Create

ropeAnchorX = 100;
ropeAnchorY = 100;
ropeLength = 10;
velX = 0.0;
velY = 0.0;
grav = 0.5;
drag = 0.01;
restitution = 0.0;

// Step

// Apply gravity
velY += grav;

//Apply drag
velX *= 1.0 - drag;
velY *= 1.0 - drag;

var nx, ny, nmag, invm, forceMag;

// [nx, ny] is the vector pointing from the rope anchor to the position of the object after applying velocity
nx = x + velX - ropeAnchorX;
ny = y + velY - ropeAnchorY;

// Check if the object will be within the rope range without using square root
if (sqr(nx) + sqr(ny) > sqr(ropeLength)) {
  nmag = sqrt(sqr(nx) + sqr(ny)); // The distance between the anchor and where the object would be if not for the rope
  invm = 1.0/nmag; // Calculate the reciprocal of the above; this is for optimization purposes

  // Multiply the outward vector by invm; this makes the outward vector a unit vector.
  nx *= invm;
  ny *= invm;

  // Add spring (Bugfix)
  velX -= nx * (ropeLength - nmag) / ropeLength;
  velY -= ny * (ropeLength - nmag) / ropeLength;

  // Calculate the outwards velocity
  forceMag = dot_product(nx, ny, velX, velY);
  if (forceMag > 0.0) {
    // If it's moving outwards, push the object inwards
    velX -= forceMag * nx * (1.0 + restitution);
    velY -= forceMag * ny * (1.0 + restitution);
  }

  // Correct the predicted next position after applying forces
  nx = x + velX - ropeAnchorX;
  ny = y + velY - ropeAnchorY;
}

// Set position of the object
x = nx + ropeAnchorX;
y = ny + ropeAnchorY;
 
Last edited:

ErlandSol

Member
Yeah i was a bit suprised myself that it worked so good. And ive got one question, theese variable names "nx, ny, nmag, invm, forceMag", what are they representing? I can figure out what the code does, but the names doesnt "click" in my head, if that makes sense.. Just so its a little easier to understand what is going on.
 

rytan451

Member
So nx, ny at first represent the vector from the rope anchor to the object. They are used to calculate nmag which is the magnitude of the vector n (so the distance between the rope anchor and the object). Then, invm is the inverse (reciprocal) of the magnitude nmag. Next, I multiply nx, ny by invm, making vector n a unit vector.

Finally, forceMag is the magnitude of force in the direction opposite to vector n to apply to the object, such that when the object moves, it doesn't stretch the "rope".
 
Top