# How to implement a tractor beam?

I'm working on a game where the player can pick up objects using something like a tractor beam, and carry them around.

Attracting the object towards the center of the beam is not difficult. But once the object is close enough to the center, I need to keep it there while the player moves, which is what I'm having problems with. I can think of two ways to do it, and both have issues:

1. Update the object's position whenever the player position changes, keeping it centered on the beam.

2. Update the object's velocity to point directly towards the center of the beam, the further away, the more velocity.

Moving and rotating works just fine with both approaches, but the physics are wrong when the carried object collides with other objects:

With the first approach, physics are completely ignored. The carried object just pushes anything out of the way. That is because position changes are only supposed to be done as part of the world physics, based on velocity.

With the second approach, physics basically behave how they should, but overreact. The problem is: In order to keep the carried object at the center of the beam even when rotating and moving, I need to use high velocity values. So once the carried object touches another one, it gets way too much velocity from the collision.

How can I implement this properly? My best guess right now is to go with the second approach and add special handling for carried objects to the world physics, reducing the velocity to sane values for collisions or when the player stops carrying them. But that seems like a pretty inelegant workaround.

Edit: Adding some pseudo code to illustrate how it works right now (that'd be the second approach above)

void attract_object(object, ticks) {
Vector distance = beam_center - object.center;
// If the object is not close to the beam center, attract it slowly
if (magnitude(distance) > 10) {
object.velocity += distance.normalized() * ticks * 0.1;
return;
}

// Here comes the part we're talking about. That magic 0.5 is just high enough
// that the object isn't lost while moving around. But it's still so high that
// other objects are repelled with way too much force.
object.velocity = distance * ticks * 0.5;
}


From what I see, this happens when the carried object pushes another object away:

1. The carried object collides with another object
2. The objects' velocities are distributes properly, so the carried object is pushed away from the center of the beam in the process
3. The code above causes the carried object to return to the center of the beam, with so much velocity that it'll get back there quickly
4. When the carried object moves back towards the center of the beam, half of its high velocity is transferred to the other object, repelling it violently. Since the initial velocity of the carried object appears to be sane, I can imagine that steps 2 to 4 are repeated multiple times, building up such a high velocity.

That appears to be the cause. I can't think of a nice way to fix it though :(

• Welcome to the complexities of a Tokomak fusion reactor. You have the benefit of only needing to build a working mathematical model, not the working magnetic bottle, but the mathematics is identical and non-trivial. What you are attempting is do-able, but you will need to think through your mathematical model closely before coding. – Pieter Geerkens Sep 8 '13 at 20:03

Essentially what you are looking for is have the 'beamed' object behave exactly as if you would grab it with your hands.
One option would be to make it share the acceleration a/o speeds of the 'hand' that holds it instead of adjusting its velocity to fill the gap with the center of the beam.

Let's say the center of the beam is the holding hand. if your character rotates 90 degrees to his left in 1 second, then the velocity of the hand would be:

 If R = length of the arm: which is the radius of the rotation circle R^2 *PI /4 would be the distance traveled over a second.  make it times the elapsedTime of the frame to find the speed you must apply to your object. Find the horizontal normal to the beam to find its direction vector.

My point is that you don't have to solve problems if you try other implementations that wont cause it in the first place.

I would go play with the gravity gun in HL2 to get some inspiration on the problem, but i have other plans for today.

EDIT: im sorry i thought it was for a 3D beamgun, but its essentially the same thing with 2D except the axes are different (and there is no complex geometry)

• After trying various hacks, this is what got me on track, the result is looking good. The velocity on collision is still a tad bit too high, but I think I can figure that out. Possibly just by not attracting any objects with a high velocity in a different direction. – futlib Jan 15 '14 at 17:22
• Maybe when the 'hand' is colliding with a wall you can compute the nearest plausible (thats not colliding) position for the hand and use this as the 'temporary hand' while collision occurs. I often like to engage problems from another perspective. Im glad i could help with that ;). – icosamuel Jan 21 '14 at 20:13

How about adding a spring connection, i.e. force the object carried to move back to the carry position based on the distance, but still allow it to be pushed away by solid things (like walls).

Constantly adjust the carried object's velocity (by changing acceleration based on position/distance) to point towards the position of the tractor beam (i.e. your second approach). If the object is pushed away too far, drop the connection (and the object).

I'm not really sure why you'd need high velocities. Especially the "player lets go" case would indicate that your rotation velocity might be too high or unrealistic. Also don't forget things such as air resistance and gravity.

Edit: Considering the updated code, the problem is rather trivial to find:

if (magnitude(distance) > 10) {
object.velocity += distance.normalized() * ticks * 0.1;
return;
}


The problem here is the case where the object's distance to its goal position is constantly too far (i.e. > 10). As long as this condition is true, it's velocity simply increases over and over again (i.e. indefinitely).

Two possible solutions for this:

Define a maximum velocity:

object.velocity = min(object.velocity + distance.normalized() * ticks * 0.1, max_velocity);


Apply a fixed velocity rather than accelerating:

object.velocity = distance.normalized() * ticks * magic_factor;


Just accelerating while being too far away is definitely a wrong approach here. Thing about pulling a spring or rubber band: It won't matter whether you hold it for one second or one minute. In the end it will accelerate the same way (considering it hasn't been in motion and there are no other forces applied).

• That's what I described in method 2, isn't it? That's basic spring physics AFAIK. I'll add some code to the question above to illustrate it. – futlib Sep 7 '13 at 22:06
• Also removed the part about incorrect velocity above, it's actually fine, just tested it. So it's just the collisions with other objects that are messed up. – futlib Sep 7 '13 at 22:14
• Yes it's essentially your second approach. Updating my answer. – Mario Sep 8 '13 at 11:05
• Tried both approaches, but it doesn't help. It looks like the magnitude(distance) > 10 case is not the culprit here. I tried limiting the velocity for the <= case, but it's the usual problem: The velocity is either so low that the object is being dropped, or so high that it repels others violently. – futlib Sep 8 '13 at 11:49
• "so high that it repels others violently": You should move other objects based on the effective velocity, not the velocity behind the scenes (i.e. reset velocity due to collision). – Mario Sep 8 '13 at 11:52