Jump to content


Photo

Springs between objects


  • Please log in to reply
12 replies to this topic

#1 quattj

quattj

    GMC Member

  • New Member
  • 74 posts

Posted 16 January 2011 - 06:31 PM

I'm making a creature in a side scroller made of multiple segments (serpent). I'm trying to set up springs between the body segments but can't figure out how to apply the math to it. I have a head that will be doing the main motion, so is using its own hspeed and vspeed (or speed/direction, depending on what works better with the math involved). On the other end will be a piece that's either anchored to a wall or free floating. All body segments need springs pulling them towards the segment in front while not getting too close, and all but the last body piece need springs also pulling them towards the segment in back.

I've been looking at a couple of examples people made of a stretchy polygon that maintains its volume as it falls, bounces, etc. I tried deconstucting it and just using the parts I think I need, but the result is always body parts that either don't budge, fall off the screen, or fly off in crazy directions as soon as the head moves. I might have parts of the volume and gravity calculations left over from the example that are throwing it off.

I know F = -k*dx is the main formula for the sping force. I'm pretty sure that F is going to be a modifier for my speed and direction variables for each body part, so each body part would have a "dv" (change in speed) and "dd" (change in direction) applied on it from the piece to either side (or a "dhspeed" and "dvspeed" to change the hspeed and vspeed).

My "dx" should be "point_distance(body1.x, body1.y, body2.x, body2.y) - def_length"
"k" I take to be how strong I want the spring to be, so I'll have to find a value I like.

How do I then convert my "F" and properly split it into either an hspeed and vspeed, or a dv and dd, acting on the body part?
  • 0

#2 Erik Leppen

Erik Leppen

    GMC Member

  • GMC Member
  • 2038 posts
  • Version:GM:Studio

Posted 16 January 2011 - 06:45 PM

Force is a vector quantity - so it has a size and a direction. The size of the force is -k * dx, and the direction of the force is the direction between the end points of the spring.

As soon as you have a size and a direction, I think you can now figure out what to do with them :)
  • 0

#3 quattj

quattj

    GMC Member

  • New Member
  • 74 posts

Posted 16 January 2011 - 08:19 PM

As soon as you have a size and a direction, I think you can now figure out what to do with them :)

Light bulb! Ting! It's been such a long time since I dealt with math like this, I've forgotten useful tidbits like that. I've been writing everything out on paper for the past hour or so and think I have a much better way of setting up my objects, and now I can sort out that pesky direction issue I think. Thanks! I'll follow up later with my results.
  • 0

#4 quattj

quattj

    GMC Member

  • New Member
  • 74 posts

Posted 17 January 2011 - 09:12 AM

Okay, after lots of tinkering, this is what I ended up with.
I have a "tail" (end piece) which creates the "body" pieces, and finally the "head".

The tail is in a fixed position.
The head can move about freely.
"follow" is the ID of the previous body part (closer to head).
"trail" is the ID of the next body part (closer to tail).
"spacing" is the starting space between parts, which is the equilibrium spring length.

In the step event of the body pieces:

f1 = -k * (point_distance(follow.x, follow.y, x, y) - spacing)
f2 = -k * (point_distance(trail.x, trail.y, x, y) - spacing)
if (f1 >= 0)
  dir1 = point_direction(x, y, follow.x, follow.y)
else if (f1 < 0)
  dir1 = point_direction(follow.x, follow.y, x, y)
if (f2 >= 0)
  dir2 = point_direction(x, y, trail.x, trail.y)
else if (f2 < 0)
  dir2 = point_direction(trail.x, trail.y, x, y)
dx1 = lengthdir_x(f1, dir1)
dx2 = lengthdir_x(f2, dir2)
dy1 = lengthdir_y(f1, dir1)
dy2 = lengthdir_y(f2, dir2)
dx = dx1 + dx2
dy = dy1 + dy2
x += dx
y += dy
It seems to work very well, with two issues:
- I need to know how to add in dampening to eliminate piece wiggle.
- If the pieces get too close to one another, they snap on top of one another and twitch around like crazy. This is affected by the spring length, so I think there's an issue possibly if the distance between parts is smaller than the spring length.

Any suggestions on those two items? Both issues become unnoticeable if I use a very small (like "1") spring length, but I'd like to do it properly if possible.
  • 0

#5 Erik Leppen

Erik Leppen

    GMC Member

  • GMC Member
  • 2038 posts
  • Version:GM:Studio

Posted 17 January 2011 - 12:10 PM

if (f1 >= 0)
  dir1 = point_direction(x, y, follow.x, follow.y)
else if (f1 < 0)
  dir1 = point_direction(follow.x, follow.y, x, y)


You shouldn't do this. A few lines below, you're taking lengthdir_x(f1, dir1) and lengthdir_y(f1, dir1). If distance < spacing, f1 wil be negative, and then (lengthdir_x(f1, dir1), lengthdir_y(f1, dir1)) will automatically "turn around" too, because moving a negative distance in a direction moves you in the opposite direction. So you do not have to turn around the direction yourself, this is automatically done by this system. So replace the above code by

dir1 = point_direction(x, y, follow.x, follow.y)
Same goes for trail.

Probably this is what causes things to go weird when pieces get too close. When the spring is longer than spacing, lengthening it will generate pulling force, however because both you and GM switch signs when spring is shorter than spacing, this will cause a further shortening of the spring to generate pulling force also, which is not what you want.

- I need to know how to add in dampening to eliminate piece wiggle.

I usually just put
dx *= 0.9; dy *= 0.9
(or some similar constant) in the step event to simulate friction.
  • 0

#6 Yourself

Yourself

    The Ultimate Pronoun

  • Retired Staff
  • 7341 posts
  • Version:Unknown

Posted 17 January 2011 - 07:02 PM

So you do not have to turn around the direction yourself, this is automatically done by this system.


You don't even have to work with angles, either. In fact, that's a waste of time, since you're just using lengthdir to get it back into rectangular coordinates. But you already have rectangular coordinates, since that's how you got the direction in the first place.
  • 0

#7 quattj

quattj

    GMC Member

  • New Member
  • 74 posts

Posted 17 January 2011 - 11:08 PM

too, because moving a negative distance in a direction moves you in the opposite direction. So you do not have to turn around the direction yourself, this is automatically done by this system. So replace the above code by

If I take out my direction reversal, as soon as the head moves, the body pieces go flying off the screen, alternating starting at the head, and moving down the line, with one piece flying off one side, the next off the other, etc. along the initial axis of movement.

I think it's because the point_distance function I use to find the direction doesn't take sign into account, so I have to compensate for it.

You don't even have to work with angles, either. In fact, that's a waste of time, since you're just using lengthdir to get it back into rectangular coordinates. But you already have rectangular coordinates, since that's how you got the direction in the first place.

How would I do it otherwise? Is there a force equation that works with rectangular coordinates? I thought maybe doing a four force calculation with just the x and just the y so I'd have an x and y force for each spring, but then I'd still need the angle of the spring to figure out the force "length" in each direction.

Originally, before the spring math got involved, I was using a simple

x = (follow.x + trail.x) / 2 
y = (follow.y + trail.y) / 2

but that only moves the pieces directly between the other bits, and doesn't give the fluidity I'd like.
  • 0

#8 Gamer3D

Gamer3D

    Human* me = this;

  • GMC Member
  • 1591 posts
  • Version:GM8.1

Posted 17 January 2011 - 11:52 PM

Use vectors.

Assuming a system with time step t, (velocities and forces relative to a time unit. A time step of 0.5 moves everything by 1/2 of a time unit)
Given two points with positions [x1, y1] and [x2, y2], with velocities [dx1, dy1] and [dx2, dy2] and a spring with spring constant k, resting length len, and maximum friction force f:

Compute the difference between positions [diff_x, diff_y] = [x2 - x1, y2 - y1]
The length of this vector is dist = sqrt(diff_x * diff_x + diff_y * diff_y)
The force to apply is (dist - len) * k. We create a vector of this length by multiplying a unit vector by this scalar. [force_x, force_y] = [diff_x, diff_y] * ((dist - len) * k / dist)
dx1 += force_x * t / 2;
dy1 += force_y * t / 2;
dx2 -= force_x * t / 2;
dy2 -= force_y * t / 2;

Now friction (only along the length of the vector):
[rel_dx, rel_dy] = [dx2 - dx1, dy2 - dy1]
spd = (rel_dx * diff_x + rel_dy * diff_y) / dist;
max_friction_change = abs(f * t); // Remove the absolute values if you can be sure no prankster is going to set negative friction or time.
if (spd > max_friction_change)
spd = max_friction_change;
if (spd < -max_friction_change)
spd = -max_friction_change;
// Now apply an equal and opposite force to both.
dx1 += spd * diff_x / dist / 2;
dy1 += spd * diff_y / dist / 2;
dx2 -= spd * diff_x / dist / 2;
dy2 -= spd * diff_y / dist / 2;


So yeah. The basic idea is: Use vectors, use Hooke's law, have fun.
  • 0

#9 quattj

quattj

    GMC Member

  • New Member
  • 74 posts

Posted 18 January 2011 - 08:34 PM

f1 = -k * (point_distance(follow.x, follow.y, x, y) - spacing)
f2 = -k * (point_distance(trail.x, trail.y, x, y) - spacing)
dir1 = point_direction(follow.x, follow.y, x, y)
dir2 = point_direction(trail.x, trail.y, x, y)
dx1 = lengthdir_x(f1, dir1)
dx2 = lengthdir_x(f2, dir2)
dy1 = lengthdir_y(f1, dir1)
dy2 = lengthdir_y(f2, dir2)
dx = dx1 + dx2
dy = dy1 + dy2
x += dx
y += dy
image_angle = point_direction(x, y, follow.x, follow.y) - 180
That'll do it. You were correct, Erik, about not needing to reverse the angle, but the problem was I had my point_distance set up facing the wrong way. It works beautifully now.

And as for Gamer3D, I am technically not using vecters, but I am using their components. And, err, it's been so long I don't remember all of that "fancy" stuff :P I will pore through your example, however, and if I can adapt it I will see which method is more efficient for me.

Thanks!
  • 0

#10 Gamer3D

Gamer3D

    Human* me = this;

  • GMC Member
  • 1591 posts
  • Version:GM8.1

Posted 19 January 2011 - 04:02 AM

And as for Gamer3D, I am technically not using vecters, but I am using their components. And, err, it's been so long I don't remember all of that "fancy" stuff :P I will pore through your example, however, and if I can adapt it I will see which method is more efficient for me.

Hahaha. How do you think vector operations are done?

FYI, I got distracted partway through writing my example, so it's probably not the best thing to learn from.
  • 0

#11 name

name

    GMC Member

  • GMC Member
  • 1427 posts

Posted 17 February 2011 - 03:51 AM

f1 = -k * (point_distance(follow.x, follow.y, x, y) - spacing)
f2 = -k * (point_distance(trail.x, trail.y, x, y) - spacing)
dir1 = point_direction(follow.x, follow.y, x, y)
dir2 = point_direction(trail.x, trail.y, x, y)
dx1 = lengthdir_x(f1, dir1)
dx2 = lengthdir_x(f2, dir2)
dy1 = lengthdir_y(f1, dir1)
dy2 = lengthdir_y(f2, dir2)
dx = dx1 + dx2
dy = dy1 + dy2
x += dx
y += dy
image_angle = point_direction(x, y, follow.x, follow.y) - 180


I'm sorry to resurrect this thread, but could someone comment this?

I'm trying to figure out how it works, and would really appreciate it.
  • 0

#12 quattj

quattj

    GMC Member

  • New Member
  • 74 posts

Posted 17 February 2011 - 04:12 AM


f1 = -k * (point_distance(follow.x, follow.y, x, y) - spacing)
f2 = -k * (point_distance(trail.x, trail.y, x, y) - spacing)
dir1 = point_direction(follow.x, follow.y, x, y)
dir2 = point_direction(trail.x, trail.y, x, y)
dx1 = lengthdir_x(f1, dir1)
dx2 = lengthdir_x(f2, dir2)
dy1 = lengthdir_y(f1, dir1)
dy2 = lengthdir_y(f2, dir2)
dx = dx1 + dx2
dy = dy1 + dy2
x += dx
y += dy
image_angle = point_direction(x, y, follow.x, follow.y) - 180


I'm sorry to resurrect this thread, but could someone comment this?

I'm trying to figure out how it works, and would really appreciate it.

This code sets up two spring forces acting on the object. 'follow' and 'trail' are the two objects it is connected to. The forces [f1 and f2] point toward the object if they have a positive value.
point_distance gives the current length of the spring.
'spacing' is the "at rest" spring length.
dir1 and dir2 are the direction each spring is pulling. dx and dy are the amount of x and y movement each spring would cause by itself, added together to get the actual movment of the object.
I will clarify further if that still doesn't make sense. Typing with a wiimote right now. Not the best way to reply to a complicated post :P
  • 1

#13 name

name

    GMC Member

  • GMC Member
  • 1427 posts

Posted 17 February 2011 - 04:26 AM

Thank you. I understand now.

Also, I need to commend you on the use of a wii-mote to type anything. That's hard to do.
  • 0




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users