- Home /
Follow a curve using physics or transform manipulation
I'm in the early stages of a new game, and need help developing the movement.
The problem boils down to this: Imagine a donut shaped tunnel (i.e. a cyclotron/torus), with objects that move('float') within the lumen of tunnel. I'd like those objects to continuously circle around the tunnel. I can simuluate the motion using splining, but there will be many objects flying around, and I like them to float through the tunnel more randomly/realistically, using either physics or direct manipulation of the transforms.
I've tried just using physics, and applying a forward force, but eventually the object just ends up sliding along the outer wall of the tunnel (see this link http://www.orgoquest.com/unitytest/temp.html ).
My latest idea was to have the object move forward along it's z-axis, do a raycast to the right from the object to the outer wall, get the normal of that hit point, and rotate the object so that it's z axis is directed 90 degrees to the normal, and then move forward along the z-axis. In my mind, this would cause the object to move around the curve at a constant distance from the outer wall, but it's not working. Maybe because I'm doing something wrong with the vector math.
I was hoping someone could point me in the right direction to try to accomplish this, either using the idea above, or anything else out there.
Thanks.
Edit: Although I'm using a torus now, in the future the shape will be a tunnel of arbitrary direction, not necessarily in a circle. The way the cells move in this video http://www.youtube.com/watch?v=tKgroDE4DHo&feature=player_embedded at the 46-49 second mark is what I'm trying to recreate.
Answer by skovacs1 · Sep 15, 2010 at 08:58 PM
Moving inside a torus
I never took Bio, so while I correctly assumed the meaning of lumen in this context, I had to look it up to be sure.
Because the shape is constant, we can make some assumptions and do some simple calculations to make this work because we're just moving in a circle.
Without physics
The simple solution is to center the pivot of motion at the center of the torus with the object positioned inside the tunnel and then just rotate. This can be done with some simple parenting and rotating the parent, changing the pivot itself or just getting the center of the torus which is quite easy especially if the parent is the torus.
//assuming the parent is at the center of the torus
transform.RotateAround(transform.parent.position, transform.parent.up, Time.deltaTime*speed);
With physics
To fly in a circle with forces is a pain. You must constantly apply a relative force in the direction that you are turning, so you have to keep track of this direction. Something like turning the object with torque, using drag, no gravity and no angular drag:
function Start() { rigidbody.AddRelativeTorque(Vector3.up * 1000); }
function FixedUpdate() { rigidbody.AddRelativeForce (Vector3.forward * 1, ForceMode.Impulse); }
The problem is that you can't really control the circle terribly well and if you get moved off your center, it becomes trying to get it back under control.
As an alternative, you could also apply a sort of "gravity" towards the center of the torus as well, to keep the object's rotation under control, but this becomes a bit harder to maintain as a circle.
//With no drag, this should give you a circle //With drag, you have to add force every update to move forward function Start() { rigidbody.AddRelativeForce(Vector3.right * 10000); }
function FixedUpdate() { rigidbody.AddForce(Vector3.Normalize(transform.parent.position - transform.position) * 100); }
Using a kinematic rigidBody, you could just do something like the orbit camera script where you rotate the object and translate along an axis.
var distance : float = 10.0; var angle : float = 0.0; function FixedUpdate() { angle += Time.deltaTime*speed; while(angle >= 360) angle-= 360; //wrap around while(angle < 0) angle += 360; //wrap the other way
//Rotate around the parent's up axis
var rotation = Quaternion.AngleAxis(angle, transform.parent.up);
var position = rotation * Vector3(distance, 0.0, 0.0) + transform.parent.position;
rigidbody.MoveRotation(rotation);
rigidbody.MovePosition(position);
}
Random movement
In the natural world, there is nothing truly random, but the motion in a system that we would perceive as "random" is a product of a reasonably undetermined start motion/position + well tuned forces and adjustments. Between the complexities of force occlusion, surface interaction and pressure systems for multiple objects in even a stable medium, it's rather challenging to calculate the reality of how objects would react, so in general randomness as well as physics systems provide approximations.
Believable randomness can be a pain to create. One of the best solutions I've seen was to sample random noise (see this Unite presentation). There are too many ways to do randomness to mention, but the important part is not to directly assign random values to positions unless you know that they will smoothly interpolate. I also recommend taking a look at some of the flocking scripts as they provide some useful information on following a direction, but doing so less strictly.
Assuming you're rotated into position (which you can control by a random speed too if you wanted), you only have two axes left to worry about for random motion along the axes extending from the center and to the object and the axis you rotated around. If you orient the object relative to its orientation around the torus, it's actually a lot simpler as the two axes are actually two axes of the object's transform.
You could generate a random point along the plane formed by these axes and move towards it (or even specifically within the circle defined by the interior of your torus). You could create a speed vector of adjustment and adjust it's values by small random amounts, moving at this random vector speed, changing the speed's direction away from the walls as you get further from the calculated position so that you never hit the walls. Move randomly as your use case best dictates.
With physics forces
If you're not too concerned by the motion in the circle being a bit inaccurate, you could consider calculating the position where it should be as was done in the non-force driven methods and applying a force in that direction. You can then add random motion by either offsetting the point relative to the parent or applying a force along the plane formed by the object and the center of the torus at set intervals. You don't want to apply the random changes all the time because they can really mess up your simulation unless they are quite small.
Because this doesn't set the position explicitly, even with fairly strong random forces and collisions, as long as the object doesn't get stuck, it should continue along the initial circuit. If it does get stuck, the next time the calculations bring the destination back around, it is possible that it will re-align itself.
var distance : float = 10.0; var angle : float = 0.0;
function FixedUpdate() { angle += Time.deltaTime*speed; while(angle >= 360) angle-= 360; //wrap around while(angle < 0) angle += 360; //wrap the other way
//Rotate around the parent's up axis
var rotation = Quaternion.AngleAxis(angle, transform.parent.up);
var position = rotation * Vector3(distance, 0.0, 0.0) + transform.parent.position;
//You could randomly change position or some offset you are applying to it.
//head to where we should (Normalize if you want the adjustment more subtle)
rigidbody.AddForce((position - transform.position) * 10, ForceMode.Impulse);
//Or with a timer you could apply some force here to offset the motion
}
Moving inside arbitrary shapes
This is a bit more problematic because we can't assume much.
The general solution to this problem is to find the "forward" direction at that point in the tunnel and keep it moving that way, regardless of what else happens, assuming that your intent is to have it continue along that tunnel (like being in a stream of water, applying "constant" force from "behind"). The first problem with arbitrary shapes is how you decide what forward is and finding this can be much less obvious.
Programmatically, you could find the center/direction by performing raycasts, but with truly arbitrary shapes, assuming you aren't aligning yourself to a wall or something, you don't necessarily know which way to raycast at any given point and even if you do, it still leaves you with at least two directions (positive and negative) to use for the forward direction and very little way to really be sure which is correct.
The approach I would take is to set a center point probably with a series of waypoints and I might even set them up as bezier curves to get something smoother and I would most importantly know which way is forward by the order of the points. Using waypoints, you will of course need to know where you are along the path, which can be generally determined to some extent by your distance from the current (and subsequent/preceding if applicable) waypoints. Knowing my center point and direction, I would move my object with the path. Note that by stretching, skewing waypoint distances, you can also indicate speed adjustments based on flow, etc. Your waypoints could even be what you use to define your shape in the first place if you are generating your shape from a series of points and then your points could contain additional information about size of the shape at that point, etc.
To achieve random motion, I could do something like the above adjustments by random speeds or offsets, or set up alternate waypoints at certain points and have the object randomly interpolate between the two equations as appropriate and that would look sort of more natural while still being tunable.
As an alternative to waypoints, you could set up a series of trigger volumes that would apply force in the direction of the tunnel at that point, but this could lead to a lot of these for more complex tunnels and may get to be too much overhead depending on your use case. Also, remember to disable/enable and/or create/destroy stuff based on some LOD.
The point is that you know what forward is at any given point and push in that direction, while not immediately losing the initial motion prior to the forward motion being applied.
Sliding along the wall
If you're using physics, have you considered tuning your physic materials? Try adding things like bounciness and lowering the friction as this may be more like what you actually meant by random motion and at the very least should address your sticking to the wall some, as long as you aren't constantly pushing towards the walls.. If you do it this way, in a system without (or with very little) drag, you could apply a random force at the start and then apply forces along the tunnel's curves and narrows/expanses to adjust the direction/speed as appropriate, you can just let the physics engine drive it for you with bounces, etc.
If this doesn't resolve that issue or if you're not using physics, you could maintain a variable that stores your movement vector (or even use rigidbody velocity if you need) and, on collision calculate a reflected vector away from the surface and ensure that it moves in this vector.
//This is a rough idea of forcing your object to bounce //You may have to disable collisions for a brief moment afterwards if collisions are //happening too frequently and generating wacky behaviour
var direction : Vector3; function OnCollisionEnter(collision : Collision) { if(collision.contacts[0]) { //I'm only checking one point direction = Vector3.Reflect(-direction, collision.contacts[0].normal); //Do something with the direction here or elsewhere //Like setting the velocity on a rigidbody //or the one used in your own movement calculations } }
Thanks for the detailed response. I should have mentioned that although I'm currently using a torus for developing the movement, in the future it will be through a tunnel of arbitrary orientation, not a nice symmetrical torus now. I'll parse through your recs and see if I can wrap my head around them, esp. the last point about moving through arbitrary shapes. The way the cells move in this video http://www.youtube.com/watch?v=t$$anonymous$$groDE4DHo&feature=player_embedded at the 46-49 second mark is what I'm trying to recreate, albeit it more natural.
I've gone through the options trying to implement as needed, but likely lack the skills to figure it out (so far). I'm focusing on the moving inside arbitrary shapes and sliding along walls solutions. In sliding along the wall solution, you have direction = Vector3.Reflect(direction, collision.contacts[0].normal);...what is the direction parameter in the first argument of the Reflect method? I tried using the rigidbody.velocity, but that didn't work right. Was there something specific it should be?
Sorry. I missed a $$anonymous$$us sign. Fixed now. Inco$$anonymous$$g directional vector reflected across the surface normal will still be directed at the surface, You need to add a $$anonymous$$us sign either to the reflected direction or inco$$anonymous$$g direction in order to direct it away from the surface.
Answer by Herman-Tulleken · Sep 16, 2010 at 08:49 AM
Here is how we solved a very similar problem - we have an object hovering in a tunnel, where we control the distance between the object and the tunnel wall under the object (under here is relative to the vehicle's orientation - if the object is upside down, then the distance is between the object and the tunnel wall at the top).
There are three components:
- The system moving the object along the tunnel.
- The system keeping the object a certain distance from the tunnel wall.
- The system correcting the objects orientation (in our case, to have the object's forward vector parallel with the tunnel wall under the object). [You might not need this last one, although keeping track of this orientation will make it easier to steer your object, especially if it is also spinning].
The first is the easiest - just apply a force in the direction of the forward vector of the object (I assume that your object won't be spinning as it moves).
The second is also fairly easy. We use a spring/damper system to do this:
- Find the point where a raycast in the direction of the vehicles down vector hits the tunnel. Calculate the distance between this point and the object.
- Calculate the offset as the hit distance minus the ideal distance from the wall. To get the object in the center of the tunnel, use half the tunnel width.
- Also maintain offset offset_prev it was in the last frame.
- Now calculate a spring force F_s = -k*offset. k is a parameter you can control.
- Now calculate a damping force F_d = -c*(offset - offset_prev) / time. C is a parameter you can control, we use c = 2*sqrt(mass * k) [for critical damping], but higher values will do as well (smaller values might lead to oscillations).
- Apply the total force F_s + F_d.
(Note, you might just want to check the signs). You can find some more notes on this technique here.
The third system is a bit trickier to implement. Basically, you have to correct the pitch and roll of the object, relative to the tunnel wall under the object. This can be done as explained here. This explanation assumes that the ground is purely horizontal. To make it work for a tunnel wall, you have to convert it to the coordinates normal / tangent to the tunnel wall. This is the tricky part. We did it this way (it is somewhat clumsy, perhaps there is a better way).
Now convert your abject's orientation to the axis system of tunnelWallAxes , like this:
Vector3 newForward = tunnelWallAxes.InverseTransformDirection(rigidbody.transform.forward); Vector3 newUp = tunnelWallAxes .InverseTransformDirection(rigidbody.transform.up);
Put this in another transform (empty game object created at start-up): objectRelativeToTunnelWallAxes.transform.LookAt(newForward, newUp).
Now you can use the roll and pitch of this transform to calculate a corrective torque as explained in that answer. Although you could use damping, we did not.
Well, this is somewhat tricky to explain, I hope I at least gave you a good starting point.
Thanks for the ideas. I got through steps one and two, but the third system is above my head right now. It sounds like it should work in theory, but I can't iron it out. Thanks though for giving me something to start with.
Depending on what you want, there are several simpler alternatives to the third system. For instance, if you don't need the object to be able to loop, you could probably get away with correcting the object's rotation to the horizontal (ins$$anonymous$$d of the tunnel wall).
You could also set the vehicle forward vector to the tunnel wall-tangent directly. To prevent the object looking like it is sticking, you could add an offset vector that changes slowly over time, perhaps the tunnel-wall normal multiplied with $$anonymous$$athf.Sin(Time.timeSinceLEvelStarted * someVerySmallNumber).
Your answer
Follow this Question
Related Questions
Turning a rigidbody that's in motion 2 Answers
How do I determine which Euler Axis of an object will change when the object is rotated? 1 Answer
How to get rotation relative to the ground normal? 0 Answers
Game Object rotation sometimes doesn't match defined quaternion 0 Answers
How to set velocity to new direction after rotation? 0 Answers