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:
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

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.