Page 1 of 1

AI Rotation Direction?

Posted: Thu Jun 10, 2010 5:52 am
by GBeebe
In my game, I currently have 2 tanks. 1 controlled by the player and the other one by the AI. The "Directive" of the AI is simply to follow the player around (run into the player). Instead of moving at the angle towards the player, it must first be facing the player by rotating clockwise or counter-clockwise. Once the difference between the angle that it is facing and the angle it needs to be at is small enough, it starts moving forward. It does a decent job of following me around for the most part, but sometimes I find it just sitting there spinning around or sometimes turning the wrong way to get to the desired angle. The code is something like this:

Da = Angle of a line from AI tank to My tank
AIa = Angle that the AI tank is currently facing

Code: Select all

Ra = 180 - AIa
If (Da + Ra) < 180
  AIa - 1;clockwise
Else
  AIa + 1;counter-clockwise
EndIf
I do it this way because even though 5 is less than 350, Adding to 350 would get to 5 quicker than subtracting from it, After making sure that the angle says inbounds between 0 and 360 (Sorry, hard to explain).

Am I doing something wrong to find which direction the AI needs to be turning (it looks right on paper), or is there a another/better way?

Re: AI Rotation Direction?

Posted: Thu Jun 10, 2010 10:23 am
by pjay
It could be a number of factors throwing off your sums, it's hard to tell with the snippet you've provided.

A couple of possible gotchas:

Are your angle calculations working correctly?
Are you wrapping your tanks' angles (0 to 360)?

Re: AI Rotation Direction?

Posted: Thu Jun 10, 2010 11:27 am
by pjay
I have too much spare time at the moment: :lol:

Code: Select all

EnableExplicit

#ScreenWidth = 1024 : #ScreenHeight = 768

Structure Tank
  X.f
  Y.f
  Facing_Angle.f
  Angle_Of_Sight.f
  Turn_Speed.f
  Move_Speed.f
  To_Mouse_Angle.f
EndStructure

Global NewList Tank.Tank()

Define MyLoop.i, Number_Of_Tanks.i, Event.i, OurMouse.POINT, Diff_Angle.f, Control_Speed.f

Number_Of_Tanks.i = 10

;/ Randomize the tanks
For MyLoop = 1 To Number_Of_Tanks
  AddElement(Tank())
  Tank()\X = Random(#ScreenWidth)
  Tank()\Y = Random(#ScreenHeight)
  Tank()\Facing_Angle = Random(360)
  Tank()\Angle_Of_Sight = 20 + Random(45)
  Tank()\Move_Speed = 0.5 + (Random(100)/50.0)
  Tank()\Turn_Speed = Tank()\Move_Speed + (Random(100)/20.0)
Next


InitSprite()

OpenWindow(1,20,20,#ScreenWidth,#ScreenHeight,"Tank Test",#PB_Window_SystemMenu)
OpenWindowedScreen(WindowID(1),0,0,#ScreenWidth,#ScreenHeight,0,0,0)

Repeat

  Event = WindowEvent()

  OurMouse\X = WindowMouseX(1) : OurMouse\Y = WindowMouseY(1)

  If OurMouse\X > -1 And OurMouse\Y > -1 ;/ mouse within screen area?
    
    ForEach Tank()
      ;/ Find angle between mouse and tank
      
      Tank()\To_Mouse_Angle.f = Degree(ACos((OurMouse\X-Tank()\X) / Sqr((OurMouse\X-Tank()\X)*(OurMouse\X-Tank()\X)+(OurMouse\Y-Tank()\Y)*(OurMouse\Y-Tank()\Y))))
      If Tank()\Y < OurMouse\Y : Tank()\To_Mouse_Angle = 360 - Tank()\To_Mouse_Angle : EndIf
      Tank()\To_Mouse_Angle + 90
      
      ;/ 'fudge' the angles to get correct calculations
      Diff_Angle.f = Tank()\Facing_Angle - Tank()\To_Mouse_Angle
      If Tank()\Facing_Angle - Tank()\To_Mouse_Angle < -180 : Diff_Angle + 360 : EndIf
      If Tank()\Facing_Angle - Tank()\To_Mouse_Angle > 180 : Diff_Angle - 360 : EndIf

      
      ;/ If diff falls within tolerance, rotate & move tank
      If Abs(Diff_Angle) <= Tank()\Angle_Of_Sight
        ;/ change tank angle - using control speed to get exact turning
        Control_Speed = Tank()\Turn_Speed
        If Control_Speed > Abs(Diff_Angle) : Control_Speed = Abs(Diff_Angle) : EndIf
        If Diff_Angle < 0 : Tank()\Facing_Angle + Control_Speed : EndIf
        If Diff_Angle > 0 : Tank()\Facing_Angle - Control_Speed : EndIf
        ;/ Wrap angle around if required
        If Tank()\Facing_Angle < 0 : Tank()\Facing_Angle + 360 : EndIf
        If Tank()\Facing_Angle > 360 : Tank()\Facing_Angle - 360 : EndIf
        
        ;/ move tank based on the tanks angle
        Tank()\X + Tank()\Move_Speed * Sin(Radian(Tank()\Facing_Angle))
        Tank()\Y + Tank()\Move_Speed * Cos(Radian(Tank()\Facing_Angle))
      EndIf
    Next
    
    ;/ Draw to screen
    ClearScreen(RGB(150,150,150))
    StartDrawing(ScreenOutput()) 
      Circle(OurMouse\X,OurMouse\Y,5,255)
      ForEach Tank()
        Circle(Tank()\X,Tank()\Y,5,RGB(0,0,255)) ;/ tank

        Diff_Angle = Radian(Tank()\Facing_Angle - Tank()\Angle_Of_Sight)
        LineXY(Tank()\X,Tank()\Y,Tank()\X+(12*Sin(Diff_Angle)),Tank()\Y+(15*Cos(Diff_Angle)),RGB(0,255,0))  ;/ angle of turn 'pickup' - 
        Diff_Angle = Radian(Tank()\Facing_Angle + Tank()\Angle_Of_Sight)
        LineXY(Tank()\X,Tank()\Y,Tank()\X+(12*Sin(Diff_Angle)),Tank()\Y+(15*Cos(Diff_Angle)),RGB(0,255,0)) ;/ angle of turn 'pickup' +
        
        LineXY(Tank()\X,Tank()\Y,Tank()\X+(10*Sin(Radian(Tank()\To_Mouse_Angle))),Tank()\Y+(10*Cos(Radian(Tank()\To_Mouse_Angle))),0) ;/ angle towards mouse
        LineXY(Tank()\X,Tank()\Y,Tank()\X+(15*Sin(Radian(Tank()\Facing_Angle))),Tank()\Y+(15*Cos(Radian(Tank()\Facing_Angle))),RGB(255,255,255)) ;/ tank angle
      Next 
    StopDrawing()
    
    FlipBuffers()
  EndIf 
Until Event = #PB_Event_CloseWindow

Re: AI Rotation Direction?

Posted: Thu Jun 10, 2010 2:40 pm
by flaith
hey, really nice pjay :D

Re: AI Rotation Direction?

Posted: Thu Jun 10, 2010 8:30 pm
by GBeebe
I got it figured out, I wasn't checking to see if the angle, that the tank is facing in, is equal to the desired angle, and therefor still trying to rotate the tank, which would set it into a 360 spin. I got it now.

The procedure I'm using to figure out the angle, I had in the dusty corner of my harddrive, I think it originally came from these forums.

Code: Select all

Procedure.f gxAngle(X.f, Y.f)
;NOTE:  I did not write this Procedure.  It was origionally called FindAngle().
;I dug it out of my old code archive And have no idea where it came from.
If 0 < X.f
    ;Calculate the gradient of the line joining A and B.
    gradient.f=(-Y.f)/(X.f)
    ;Calculate the angle in radians.
    angleRads.f = ATan(gradient)
    ;Convert to degrees if required.
    angleDegs.f = 180*angleRads/#PI


If angleDegs < 0 : angleDegs + 360 : EndIf
ProcedureReturn angleDegs 

ElseIf 0 > X.f

;Calculate the gradient of the line joining A and B.
gradient.f=(Y.f)/(-X.f)
;Calculate the angle in radians.
angleRads.f = ATan(gradient)
;Convert to degrees if required.
angleDegs.f = (180*angleRads/#PI) + 180

If angleDegs < 0 : angleDegs + 360 : EndIf
ProcedureReturn angleDegs

Else ; X is 0
If 0 > Y.f
  ProcedureReturn 90
Else 
  ProcedureReturn 270
EndIf  
EndIf
EndProcedure


@pjay your calculation seems much smaller and is probably faster, I just have to figure out how to implement it.

Re: AI Rotation Direction?

Posted: Sun Jun 13, 2010 3:06 am
by kenmo
Just so you know - a native ATan2() function was recently added, so you could make a simpler calculation, such as:

Code: Select all

Procedure.f gxAngle(X.f, Y.f)
;NOTE:  I did not write this Procedure.  It was origionally called FindAngle().
;I dug it out of my old code archive And have no idea where it came from.
If 0 < X.f
    ;Calculate the gradient of the line joining A and B.
    gradient.f=(-Y.f)/(X.f)
    ;Calculate the angle in radians.
    angleRads.f = ATan(gradient)
    ;Convert to degrees if required.
    angleDegs.f = 180*angleRads/#PI


If angleDegs < 0 : angleDegs + 360 : EndIf
ProcedureReturn angleDegs

ElseIf 0 > X.f

;Calculate the gradient of the line joining A and B.
gradient.f=(Y.f)/(-X.f)
;Calculate the angle in radians.
angleRads.f = ATan(gradient)
;Convert to degrees if required.
angleDegs.f = (180*angleRads/#PI) + 180

If angleDegs < 0 : angleDegs + 360 : EndIf
ProcedureReturn angleDegs

Else ; X is 0
If 0 > Y.f
  ProcedureReturn 90
Else
  ProcedureReturn 270
EndIf 
EndIf
EndProcedure

#DegPerRad = 180.0/#PI

Procedure.f newAngle(x.f, y.f)
  Protected Angle.f = ATan2(x, -y) * #DegPerRad
  If (Angle < 0.0) : Angle + 360.0 : EndIf
  ProcedureReturn Angle
EndProcedure

Debug  gxAngle(2.0, 0.4)
Debug newAngle(2.0, 0.4)
Debug  ""

Debug  gxAngle(-3.0, 0.4)
Debug newAngle(-3.0, 0.4)
Debug  ""

Debug  gxAngle(-1.0, -2.3)
Debug newAngle(-1.0, -2.3)
Debug  ""

Debug  gxAngle(0.1, -1.7)
Debug newAngle(0.1, -1.7)
Debug  ""

Debug  gxAngle(0.0, -6.6)
Debug newAngle(0.0, -6.6)

Re: AI Rotation Direction?

Posted: Sun Jun 13, 2010 11:12 pm
by GBeebe
ATan2() does, however, seem slightly slower in timed tests. But not enough to discount it. I may end up using it instad, thanks for the tip, kenmo.