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 |