As part of an experiment I was working on, I needed to decide how an object should turn based on what it saw. Some objects would attract it, and some it would prefer to turn away from.
Turns out that’s more complicated than I expected.
So I pulled that portion out to test in an interactive example.
I’m not dissatisfied with the result, but it remains to be seen how it will work out in the larger experiment. For example, the algorithm seems overly complex. There are a whole bunch of Math.sin, Math.cos, and Math.atan2 calls. I don’t know how slow or fast these really are (I should check), but I wonder how it will fare as the number of objects increases. It’s possible that I tend to worry about that sort of thing too much: future cost.
Click to set pleasing items, and ctrl+click to set displeasing ones. You can drag them around, but the longer your mouse is down the larger they will grow, and with it their effect on the blue circle.
Press the space bar to clear them.
The darker blue dot represents where the guy is looking, and the grey line where he would like to look.
If it seems to work a little strangley, I hope that I can explain why that is the case or at least why it doesn’t so much matter (for my needs).
Currently, the blue guy can see everything. I intend to limit his sight to within a certain radius. This will prevent items at great distances from drawing his attention. I could also weight by distance, as well as size, but I’ve chosen not to do so.
Another issue is how he turns directly opposite the red dots. I’d like to limit his range of vision so that he can’t see the things that are behind him (or outside a certain angle). In that case, his goal will change as he loses sight of the red dots. My guess is that it will cause him to turn away from them, but not necessarily directly opposite them.
Working together, it is my hope that these two changes result in a movement style that seems at least somewhat natural. I guess we’ll see.
You’ve probably noticed that instead of going directly for a green dot, he goes between them. This is actually intentional (it’s for a flocking experiment). If we wanted him to go for one of them, I’d either make him head toward the closest one to him, or more likely the one closest to his goal-line.
This is how the algorithm works
The idea was to find where on the unit circle the line between the guy and each of the items intersected. Once we have that, adjust for intensity (represented here as size), and sum the resulting points.
For each of the items:
- Calculate the angle between it and the guy. This is the first Math.atan2.
- Calculate the x and y components at the item’s size (intensity) along that angle. (Math.sin, and Math.cos)
Add all the components together, and sum the intensities (because 0 is a special case where his goal is his current direction).
Calculate the new angle from the summed components, which is is the angle the guy wants to go.
After that it’s just a matter of figuring out which way the guy should turn:
var distance = (goal - current_direction) % (2 * Math.PI ); // Decide which direction to turn if ( distance > Math.PI ) distance -= 2 * Math.PI; else if ( distance < -1 * Math.PI ) distance += 2 * Math.PI; // Figure out how fur to turn var offset = Math.max( -1 * turnRadius, Math.min( distance, turnRadius ) );
Looking at that description, it seems far less complex than I remember. Which I guess is a good thing!
On another note, I noticed debugging stopped working in my Flashdevelop, and traces showed up in the swf instead of the ouput panel. I guess this was because when I installed jdk1.6 to sign for android, I downloaded the 64-bit version. Downloading and installing the 32-bit version (which was super hard to find, btw) fixed the debug issue. (After I reset JAVA_HOME to point to it.)
Still not sure what happened with the traces.