Thursday, September 26, 2013

seeker and you shall finder

Something I had to look up "how did I do that again?" in my little toy/gamemaking: have an object that will gradually turn to a target.

I made a really pretty version of this in an art game called aRTSeroids which I cribbed for this entry...

It's easy enough to get a seeker to snap instantly to its target, just do atan2(deltaY,deltaX); -- figuring out "which direction should I turn to get my angle closer to it" is a little tricker.

You can see the source here. The seeker object has an x and y propery for its position on the screen, and then an a property with its current angle. It also has a MAXTURN that sets how far it can turn per click...

  void seek(float rtx, float rty) { 
    float dx = (rtx - x);
    float dy = ( rty - y);
    //this trick seems to work to deal with the overlap..
    //they consider the shortest path even if other side of screen
    if (abs(dx) > width/2) {
      dx *= -1.0;
    if (abs(dy) > height/2) {
      dy *= -1.0;

    float wantangle = atan2(dy, dx);
    float anglediff = (a - wantangle);  
    anglediff /= PI; //I prefer thinking in multiples of PI...
    //this next bit catches the "runaround"
    if (anglediff > 1) {
      anglediff -= 2;
    if (anglediff < -1) {
      anglediff += 2;
    if (abs(anglediff) > .1) {  //arbitrary value to ignore
       if (anglediff > 0) {
        a -= smaller(abs(anglediff*PI),MAXTURN);
      } else {
        a += smaller(abs(anglediff*PI),MAXTURN);;
      } //could just be MAXTURN if you don't mind oversteer
float smaller(float a, float b){
 if(a < b) return a;
return b; 

The first trick is the "deal with the overlap". This is optional, but nice for games that feature screen wraparound-- (i.e. fly left, show up right)-- if you want to have definitive borders you can leave those lines out.

The next bit is just the hack I got to normalize the difference in the angle... esentially we try to make sure the difference between the angle it "wants" and where it is now is normalized between PI and -PI. 

Finally, that smaller() code says it will either go MAXTURN or the difference in the angle. If it just went MAXTURN, it sometimes will do a little vibrate pattern as it overshoots the angle. (Come to think of it, I think that vibration is why I did that "arbitrary value to ignore")

Anyway, the code is a little clunky and weird but it works well, and should be useful to me in future projects.

No comments:

Post a Comment