2D Directional Programming
BlitzMax Forums/BlitzMax Programming/2D Directional Programming
| ||
| I've been working on a space RPG for fun and practice a little like the Escape Velocity (http://www.ambrosiasw.com/games/evn/) series recently in Blitzmax. Using my limited knowledge of vectors/physics I managed to get the player object functioning, with rotation, inertia and acceleration. But I have run into a problem programming basic AI. How Do I made the AI ships face the player, or any object for that matter? I am at a loss working out the maths behind determining the right direction. Can anyone help? |
| ||
| Learn Trigonometry (Hint: Everything is a right angled triangle away from everything else) |
| ||
How to find the angle from the AI to the player:dx# = playerX - aiX dy# = playerY - aiY angle# = ATan2(dy,dx) You won't want the AI ship to turn instantly to face the player, so here's a function to work out the difference between two angles: Function andiff#(an1#,an2#) dan#=(an1-an2) Mod 360 If dan>180 dan:-360 If dan<-180 dan:+360 Return dan End Function So to make the AI turn at a constant rate towards a desired angle: aiAn = aiAn + turnspeed*Sgn(andiff(angle,aiAn)) Here are some links to things you might want to look at once you've got the above sorted out: fly-by-wire code how to aim at moving targets |
| ||
| Thanks warpy. I am desperately trying to use and understand your code. But its not working at all! The AI is not moving or rotating at all! Heres my code: //stuff thats inside the AI object thats of interest Method Draw() SetRotation( Direction ) DrawImage( Image,X-Player.X,Y-Player.Y ) //this is for viewpoint offset SetRotation( 0 ) EndMethod Method FacePlayer() Local dx# = Player.X - Self.X Local dy# = Player.Y - Self.Y Local angle# = ATan2(dy,dx) Self.Direction = Self.Direction + Sgn(0.1*andiff(angle,Self.Direction)) EndMethod ///////// //functions outside AI object Function andiff#(an1#,an2#) Local dan#=(an1-an2) Mod 360 If dan>180 dan:-360 If dan<-180 dan:+360 Return dan End Function In fact just doing this doesnt even work! Method FacePlayer() Local dx# = Player.X - Self.X Local dy# = Player.Y - Self.Y Local angle# = ATan2(dy,dx) Self.Direction = angle EndMethod I would muchly appreciate any help! Last edited 2011 Last edited 2011 Last edited 2011 |
| ||
| @xcessive Below is a complete code for facing an image towards a 2d point. Just copy & paste this and you should see a chessy looking tank face wherever you click your mouse. I tried coding it in your style of coding. Hopefully it will be easy for you to understand. It might take a few sec for the image to load.
SuperStrict
Graphics(640, 480, 0, 30)
SetBlend(ALPHABLEND)
Global aiImage:TImage = LoadImage(LoadBank("http::www.reflectivelayer.com/tutorials/facing/aiTank.png"))
MidHandleImage(aiImage)
Global tank:ai = New ai
While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
Cls()
DrawText("Click on a point to make tank turn in that direction", 5, 5)
If MouseHit(1)
tank.lookAtLocation(MouseX(), MouseY())
End If
tank.draw()
Flip(-1)
Wend
Type ai
Field X:Int = 320
Field Y:Int = 240
Field angle:Float
Field isTurning:Int
Field turnSpeed:Float = 5
Field turnDelta:Float
Method draw()
If Self.isTurning > 0
Self.angle:+Self.turnDelta
Self.isTurning:-1
End If
SetRotation(Self.angle)
DrawImage(aiImage, Self.X, Self.Y)
SetRotation(0)
End Method
Method lookAtLocation(tx:Int, ty:Int)
Local a:Float = getAngleToLocation(Self.X, Self.Y, tx, ty)
Self.isTurning = (a - Self.angle) / Self.turnSpeed
If Self.isTurning < 0
Self.turnDelta = -1 * Self.turnSpeed
Self.isTurning:*- 1
Else
Self.turnDelta = Self.turnSpeed
End If
End Method
End Type
Function getAngleToLocation:Float(originX:Float, originY:Float, targetX:Float, targetY:Float)
Local dx:Float = originX - targetX
Local dy:Float = originY - targetY
Return ATan2(dy, dx)
End Function
Last edited 2011 |
| ||
| zambani's code can still be improved to find the shortest angle to it's target. |
| ||
| @Jesse It does turn in the direction of the shortest angle. Is that what you mean by shortest angle to target? edit: Nevermind. I see what you mean. Thanks for pointing that out. Never noticed. Last edited 2011 Last edited 2011 |
| ||
| Just a geek note: ATAN2 is used over ATAN because ATAN2 is a computer function, which uses all 4 quadrants in the x,y grid. Trig for right triangles: Tan = opposite/adjacent angle = atan(opp/adj) angle = atan = tan^-1 I use my left hand when trying to figure out trig stuff for games. my thumb and forfinger make the angle that I'm after. The thumb is usually the X-position, and the Y position could be the finger on my right hand, used to close the gap (makes a right triangle). Then I just say the sin-oh, cos-ah, tan-oa and figure out what I need. |
| ||
| @zambani there are several post related to that same problem. I am sure they can easily be found by doing a search. I even post a solution in one of them. if need be, I can repost it. Last edited 2011 |
| ||
| @Jesse, could you be kind enough to send some links to those threads? I Couldn't find any. @zambani: The code you posted works well and I understand it. But I am at a loss as to why the tank occasionally does a 360 degree turn to get to a point right next too it :S |
| ||
| @AdamRedwoods Thanks for that tip. I always seem to end up searching for high school trigonometry sites to remind we whenever i need to calculate angles and stuff. |
| ||
| @xcessive This the issue Jesse was talking about . Imagine a circle where the 3 o'clock position is 0 and also 360 degrees . When you're at an angle of say 350 and you need to turn clockwise by 30 degrees, that would make your new angle 380 or more like 20 degrees. The numbers wrap around. Since 380 is the same as 20 and 20 is smaller than 350, my code ends up taking the long counter clockwise route. This only happens when the target causes the total angle to be more than 360. It's not that hard to fix. I just don't have the time right now to look into it. I think Jesse posted some links to possible solutions. |
| ||
| Jesse posted no such links, but hopefully he will come back and post them. I am aware that that is the problem, but Its doing my head in trying to find a solution :P. |
| ||
I seem to have got it working, I know the code is a little messy, but hey it works.
SuperStrict
Graphics 640, 480, 0,30
SetBlend(ALPHABLEND)
Global aiImage:TImage = LoadImage(LoadBank("http::www.reflectivelayer.com/tutorials/facing/aiTank.png"))
MidHandleImage(aiImage)
Global tank:ai = New ai
While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
Cls()
DrawText("Click on a point to make tank turn in that direction", 5, 5)
If MouseHit(1)
tank.lookAtLocation(MouseX(), MouseY())
End If
tank.draw()
Flip(-1)
Wend
Type ai
Field X:Int = 320
Field Y:Int = 240
Field angle:Float
Field turnSpeed:Float = 5
Field tx:Float
Field ty:Float
Method draw()
Local a:Float = getAngleToLocation(Self.X, Self.Y, tx, ty)
If Self.angle > 180
Self.angle = -180 + Abs(Self.angle - 180)
Else If Self.angle < -180
Self.angle = 180 - Abs(Self.angle - 180)
EndIf
If Abs(CalcAngle(Self.angle, a)) > Ceil(turnSpeed/2)
If CalcAngle(Self.angle, a) >= 0
Self.angle = Self.angle + turnSpeed
Else
Self.angle = Self.angle - turnSpeed
EndIf
EndIf
DrawText CalcAngle(Self.angle, a),10,90
DrawText Self.angle,10,30
DrawText a,10,60
SetRotation(Self.angle)
DrawImage(aiImage, Self.X, Self.Y)
SetRotation(0)
End Method
Method lookAtLocation(tx:Int, ty:Int)
Self.tx = tx
Self.ty=ty
End Method
End Type
Function getAngleToLocation:Float(originX:Float, originY:Float, targetX:Float, targetY:Float)
Local dx:Float = originX - targetX
Local dy:Float = originY - targetY
Return ATan2(dy, dx)
End Function
Function CalcAngle:Float(Ang1:Float,Ang2:Float) 'gets the angle difference
Local fDif:Float = Ang2-Ang1
If fDif >= 180.0
fDif :- 360.0
Else
If fDif <= -180.0
fDif :+ 360.0
EndIf
EndIf
Return fDif
End Function
|
| ||
| looks good. you don't really need it anymore but here is a link to one of the threads: http://www.blitzmax.com/Community/posts.php?topic=92164#1049126 |