|
Sin & Cos: The Programmer's Pals!: Homing Missiles Problem |
Merlin Magician
Member #16,650
March 2017
|
Dear Sirs, some weeks ago I found Amarillions great tutorial and i leand a lot from it. 1 if (new_target == true)
2 {
3 target_x = 300;
4 target_y = 400;
5 new_target = false;
6 }
the rocket misses it's target every time. Here the full code: 1#include<windows.h>
2#include<graphics.h>
3#include<cmath>
4
5// circ7
6
7void home_in();
8
9int SCREEN_W = 600;
10int SCREEN_H = 400;
11
12main(int argc, char*argv[])
13{
14 // Deklaration und Initialisierung des Grafiktreibers
15 int graphdriver, graphmode;
16 graphdriver = DETECT;
17 initgraph(&graphdriver, &graphmode, "Draw_Sine"); // graphics.h kann keine Windowstitel setzen
18
19 setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
20 cleardevice(); // Löschen des Ausgabefensters
21
22 setcolor(15); // Textfarbe weiss (15)
23 settextstyle(10, HORIZ_DIR, 2); // Fontsyle, Richtung und Grösse
24 //outtextxy(20, 120, "Sinuskurve zeichnen: "); // Textausgabe
25
26 home_in(); // Funktionsaufruf von draw_circle()
27
28 return 0;
29}
30
31void home_in ()
32{
33 // the x, y position of the homing missile
34 int x = SCREEN_W / 2;
35 int y = SCREEN_H / 2;
36 // the angle and length of the missile's velocity vector
37 int angle = 0;
38 int length = 1;
39 int angle_stepsize = 1;
40 // determines whether the missile has reached
41 // the target and a new one should be chosen
42 bool new_target = true;
43 // angle to the target
44 int target_angle;
45 // position of the target
46 int target_x;
47 int target_y;
48
49 while (!GetAsyncKeyState(VK_ESCAPE))
50 {
51 setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
52 cleardevice(); // Löschen des Ausgabefensters
53 // choose new target randomly when needed
54 if (new_target == true)
55 {
56 target_x = ((SCREEN_W + rand() % (2 * SCREEN_W)) / 4);
57 target_y = ((SCREEN_H + rand() % (2 * SCREEN_H)) / 4);
58 new_target = false;
59 }
60
61 // draw a pixel where the target is
62 putpixel ((target_x), (target_y),15);
63
64 // draw the missile
65 // (actually a circle with a line representing the angle)
66 setcolor(WHITE);
67 circle (x, y, 20);
68 line (x, y, x + (9 * cos (angle)), y + (9 * sin (angle)));
69
70 // move the missile
71 x = x + length * cos (angle);
72 y = y + length * sin (angle);
73
74 // if we are very close to the target, set a new target
75 if (abs (x - target_x) + abs (y - target_y) < 10)
76 {
77 new_target = true;
78 }
79
80 // calculate the angle from the missile to the target
81 target_angle = atan2 (target_y - y, target_x - x);
82
83 // Determine whether we should turn left or right.
84 // Note that itofix (128) represents half a circle.
85 // We use & 0xFFFFFF as a trick to get an angle
86 // between 0 and 256.
87 if (((angle - target_angle) & 0xFFFFFF) < 128)
88 {
89 angle = (angle - angle_stepsize) & 0xFFFFFF;
90 }
91 else
92 {
93 angle = (angle + angle_stepsize) & 0xFFFFFF;
94 }
95 delay (20);
96 }
97}
I applied the whole project, compressed with 7Zip, to this e-mail. Best regards and thx a lot for your great Tutorial and your help, |
Elias
Member #358
May 2000
|
You can't use int for the angles. Change them to double, i.e.: double angle = 0; double target_angle; And then just remove the stuff with 0xffffff as you are not using itofix anymore, i.e.: if (fmod(angle - target_angle, 2 * pi) < pi) { angle -= angle_stepsize; } Lastly, angle_stepsize of 1 is a very large angle, you probably want 1 * pi / 180 instead. Btw. "Grösse" is not a German word except in Switzerland/Liechtenstein, you probably want to use "Größe" for a general audience. -- |
Merlin Magician
Member #16,650
March 2017
|
Dear Elias, i changed my code, but now "rien ne vas plus". The rocket is missing its target every time?! My Code: 1void home_in ()
2{
3 // the x, y position of the homing missile
4 int x = SCREEN_W / 2;
5 int y = SCREEN_H / 2;
6 // the angle and length of the missile's velocity vector
7 double angle = 0.0;
8 int length = 1;
9 int angle_stepsize = 1* PI / 180;
10 // determines whether the missile has reached
11 // the target and a new one should be chosen
12 bool new_target = true;
13 // angle to the target
14 double target_angle;
15 // position of the target
16 int target_x;
17 int target_y;
18
19 while (!GetAsyncKeyState(VK_ESCAPE))
20 {
21 setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
22 cleardevice(); // Löschen des Ausgabefensters
23 // choose new target randomly when needed
24 if (new_target == true)
25 {
26 target_x = ((SCREEN_W + rand() % (2 * SCREEN_W)) / 4);
27 target_y = ((SCREEN_H + rand() % (2 * SCREEN_H)) / 4);
28 new_target = false;
29 }
30
31 // draw a pixel where the target is
32 putpixel ((target_x), (target_y),15);
33
34 // draw the missile
35 // (actually a circle with a line representing the angle)
36 setcolor(WHITE);
37 circle (x, y, 20);
38 line (x, y, x + (9 * cos (angle)), y + (9 * sin (angle)));
39
40 // move the missile
41 x = x + length * cos (angle);
42 y = y + length * sin (angle);
43
44 // if we are very close to the target, set a new target
45 if (abs (x - target_x) + abs (y - target_y) < 10)
46 {
47 new_target = true;
48 }
49
50 // calculate the angle from the missile to the target
51 target_angle = atan2 (target_y - y, target_x - x);
52
53 if (fmod(angle - target_angle, 2*PI) < PI)
54 {
55 angle = (angle - angle_stepsize) ;
56 }
57 else
58 {
59 angle = (angle + angle_stepsize) ;
60 }
61 delay (20);
62 }
63}
Thanx a lot for your advice, |
Audric
Member #907
January 2001
|
int angle_stepsize = 1* PI / 180; This has a value of exactly zero, because the computation gives around 0.017 and then you store it as an integer. Change the variable to a double. Also, you may want to get the habit of typing literal numbers with a decimal point when you want floating computation (ie 1.0 * PI / 180.0). Here you have no problem, but there is a huge difference between 1/2 * x and 1.0/2.0 * x. Edit: Edit: In fact you'll also need to set x and y as doubles, because your steps are tiny (1 pixel), so this part : will modify x or y by a maximum of 1 pixel at a time. |
amarillion
Member #940
January 2001
|
Great, it's nice to see my tutorial is still useful to people! Audric has the right answer for your problem. Be careful for rounding floating point values. The tutorial recommends fixed point values, which is a bit outmoded and also makes it all Allegro 4 specific. Perhaps I should update it one of these days. Nowadays, I would say (directly contradicting the tutorial): use double/float instead of fixed. If you manage to get the code right for the various graphics library, I can reference them in the article if you like. -- |
Merlin_MLN
Member #16,654
March 2017
|
Dear Audric and Amarillion, i changed everything to double but now, sadly the rocket flies in a circle and never hits the target.?????? @Amarillion Here the code: 1#include<windows.h>
2#include<graphics.h>
3#include<cmath>
4
5#define PI 3.141592654
6
7// circ7
8
9void home_in();
10
11int SCREEN_W = 600;
12int SCREEN_H = 400;
13
14main(int argc, char*argv[])
15{
16 // Deklaration und Initialisierung des Grafiktreibers
17 int graphdriver, graphmode;
18 graphdriver = DETECT;
19 initgraph(&graphdriver, &graphmode, "Draw_Sine"); // graphics.h kann keine Windowstitel setzen
20
21 setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
22 cleardevice(); // Löschen des Ausgabefensters
23
24 setcolor(15); // Textfarbe weiss (15)
25 settextstyle(10, HORIZ_DIR, 2); // Fontsyle, Richtung und Größe
26 //outtextxy(20, 120, "Sinuskurve zeichnen: "); // Textausgabe
27
28 home_in(); // Funktionsaufruf von home_in()
29
30 return 0;
31}
32
33void home_in()
34{
35 // the x, y position of the homing missile
36 double x = SCREEN_W / 2;
37 double y = SCREEN_H / 2;
38 // the angle and length of the missile's velocity vector
39 double angle = 0;
40 double length = 1;
41 double angle_stepsize = 1 * PI / 180;
42 // determines whether the missile has reached
43 // the target and a new one should be chosen
44 bool new_target = true;
45 // angle to the target
46 double target_angle;
47 // position of the target
48 double target_x;
49 double target_y;
50
51 while (!GetAsyncKeyState(VK_ESCAPE))
52 {
53 setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
54 cleardevice(); // Löschen des Ausgabefensters
55 // choose new target randomly when needed
56 if (new_target == true)
57 {
58 target_x = ((SCREEN_W + rand() % (2 * SCREEN_W)) / 4);
59 target_y = ((SCREEN_H + rand() % (2 * SCREEN_H)) / 4);
60 new_target = false;
61 }
62
63 // draw a pixel where the target is
64 putpixel ((target_x), (target_y),15);
65
66 // draw the missile
67 // (actually a circle with a line representing the angle)
68 setcolor(WHITE);
69 circle (x, y, 20);
70 line (x, y, x + (9 * cos (angle)), y + (9 * sin (angle)));
71
72 // move the missile
73 x = x + length * cos (angle);
74 y = y + length * sin (angle);
75
76
77 if (abs (x - target_x) + abs (y - target_y) < 10)
78 {
79 new_target = true;
80 }
81
82 // calculate the angle from the missile to the target
83 target_angle = atan2 (target_y - y, target_x - x);
84
85
86 if (fmod(angle - target_angle, 2*PI) < PI)
87 {
88 angle = (angle - angle_stepsize) ;
89 }
90 else
91 {
92 angle = (angle + angle_stepsize) ;
93 }
94 delay (1);
95 }
96}
Thanks for for help and advice, |
amarillion
Member #940
January 2001
|
It's possible that if the turning speed (i.e. angle_stepsize) is slow compared to the speed (i.e. length), you can never turn fast enough to reach the target. (Think of the international space station constantly turning towards earth, but missing it because it moves so fast) Try increasing the angle_stepsize, or decreasing the speed (-> length). Does that help? -- |
Merlin_MLN
Member #16,654
March 2017
|
No Amarillion, the rocket is still flying on a circle! 1#include<windows.h>
2#include<graphics.h>
3#include<cmath>
4
5#define PI 3.141592654
6
7// circ7
8
9void home_in();
10
11int SCREEN_W = 600;
12int SCREEN_H = 400;
13
14main(int argc, char*argv[])
15{
16 // Deklaration und Initialisierung des Grafiktreibers
17 int graphdriver, graphmode;
18 graphdriver = DETECT;
19 initgraph(&graphdriver, &graphmode, "Draw_Sine"); // graphics.h kann keine Windowstitel setzen
20
21 setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
22 cleardevice(); // Löschen des Ausgabefensters
23
24 setcolor(15); // Textfarbe weiss (15)
25 settextstyle(10, HORIZ_DIR, 2); // Fontsyle, Richtung und Größe
26 //outtextxy(20, 120, "Sinuskurve zeichnen: "); // Textausgabe
27
28 home_in(); // Funktionsaufruf von home_in()
29
30 return 0;
31}
32
33void home_in()
34{
35 // the x, y position of the homing missile
36 double x = SCREEN_W / 2;
37 double y = SCREEN_H / 2;
38 // the angle and length of the missile's velocity vector
39 double angle = 0;
40 double length = 0.1;
41 double angle_stepsize = 1 * PI / 180;
42 // determines whether the missile has reached
43 // the target and a new one should be chosen
44 bool new_target = true;
45 // angle to the target
46 double target_angle;
47 // position of the target
48 double target_x;
49 double target_y;
50
51 while (!GetAsyncKeyState(VK_ESCAPE))
52 {
53 setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
54 cleardevice(); // Löschen des Ausgabefensters
55 // choose new target randomly when needed
56 if (new_target == true)
57 {
58 target_x = ((SCREEN_W + rand() % (2 * SCREEN_W)) / 4);
59 target_y = ((SCREEN_H + rand() % (2 * SCREEN_H)) / 4);
60
61 new_target = false;
62 }
63
64 // draw a pixel where the target is
65 putpixel ((target_x), (target_y),15);
66
67 // draw the missile
68 // (actually a circle with a line representing the angle)
69 setcolor(WHITE);
70 circle (x, y, 20);
71 line (x, y, x + (9 * cos (angle)), y + (9 * sin (angle)));
72
73 // move the missile
74 x = x + length * cos (angle);
75 y = y + length * sin (angle);
76
77
78 if (fabs (x - target_x) + abs (y - target_y) < 10)
79 {
80 new_target = true;
81 }
82
83 // calculate the angle from the missile to the target
84 target_angle = atan2 (target_y - y, target_x - x);
85
86 if ((angle - target_angle, 2*PI) < PI)
87 {
88 angle = (angle - angle_stepsize) ;
89 }
90 else
91 {
92 angle = (angle + angle_stepsize) ;
93 }
94 delay (1);
95 }
96}
Thanx a lot for your advice, |
Elias
Member #358
May 2000
|
Quote: if ((angle - target_angle, 2*PI) < PI) Did you lose the fmod there? The idea is this: angle = 90, target_angle = 0, fmod(90, 360) = 90 -> angle = angle - 1 = 89
angle = 270, target_angle = 0, fmod(270, 360) = 270 -> angle = angle + 1 = 271
angle = 0, target_angle = 90, fmod(-90, 360) = 270 -> angle = angle - 1 = 359
Without the fmod you always turn left and fly in a circle. Did you actually read the original article? -- |
Merlin_MLN
Member #16,654
March 2017
|
Dear Sirs, here is a working version of the code (with fixed)!!!! @Elias Thanks for your help and advice, 1#include<windows.h>
2#include<graphics.h>
3#include<cmath>
4#include <iostream>
5using namespace std;
6
7// circ7
8
9void home_in();
10
11int SCREEN_W = 600;
12int SCREEN_H = 400;
13
14main(int argc, char*argv[])
15{
16 // Deklaration und Initialisierung des Grafiktreibers
17 int graphdriver, graphmode;
18 graphdriver = DETECT;
19 initgraph(&graphdriver, &graphmode, "Draw_Sine"); // graphics.h kann keine Windowstitel setzen
20
21 setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
22 cleardevice(); // Löschen des Ausgabefensters
23
24 setcolor(15); // Textfarbe weiss (15)
25 settextstyle(10, HORIZ_DIR, 2); // Fontsyle, Richtung und Grösse
26 //outtextxy(20, 120, "Sinuskurve zeichnen: "); // Textausgabe
27
28 home_in(); // Funktionsaufruf von draw_circle()
29
30 return 0;
31}
32
33void home_in ()
34{
35 // the x, y position of the homing missile
36 double x = SCREEN_W / 2;
37 double y = SCREEN_H / 2;
38 // the angle and length of the missile's velocity vector
39 int angle = 0;
40 int length = 1;
41 int angle_stepsize = 1;
42 // determines whether the missile has reached
43 // the target and a new one should be chosen
44 bool new_target = true;
45 // angle to the target
46 int target_angle;
47 // position of the target
48 int target_x;
49 int target_y;
50
51 while (!GetAsyncKeyState(VK_ESCAPE))
52 {
53 setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
54 cleardevice(); // Löschen des Ausgabefensters
55 // choose new target randomly when needed
56 if (new_target == true)
57 {
58 target_x = ((SCREEN_W + rand() % (2 * SCREEN_W)) / 4);
59 target_y = ((SCREEN_H + rand() % (2 * SCREEN_H)) / 4);
60 new_target = false;
61 }
62
63 // draw a pixel where the target is
64 putpixel ((target_x), (target_y),15);
65
66 // draw the missile
67 // (actually a circle with a line representing the angle)
68 setcolor(WHITE);
69 circle (x, y, 20);
70 line (x, y, x + (9 * cos (angle)), y + (9 * sin (angle)));
71
72 // move the missile
73 x = x + length * cos (angle);
74 y = y + length * sin (angle);
75
76 // if we are very close to the target, set a new target
77 if (abs (x - target_x) + abs (y - target_y) < 10)
78 {
79 new_target = true;
80 }
81
82 // calculate the angle from the missile to the target
83 target_angle = atan2 (target_y - y, target_x - x);
84
85 // Determine whether we should turn left or right.
86 // Note that itofix (128) represents half a circle.
87 // We use & 0xFFFFFF as a trick to get an angle
88 // between 0 and 256.
89 if (((angle - target_angle) & 0xFFFFFF) < 128)
90 {
91 angle = (angle - angle_stepsize) & 0xFFFFFF;
92 }
93 else
94 {
95 angle = (angle + angle_stepsize) & 0xFFFFFF;
96 }
97 // cout << "Winkel:" << angle ;
98 delay (10);
99 }
100}
|
Niunio
Member #1,975
March 2002
|
amarillion said: The tutorial recommends fixed point values, which is a bit outmoded and also makes it all Allegro 4 specific. Perhaps I should update it one of these days. Nowadays, I would say (directly contradicting the tutorial): use double/float instead of fixed. Just comment that I find fixed-point still useful. You don't need to check values to know if angle overflows the circle, you can do fast calculations with bit operations and if you need to get a lot of cos/sin values in a row the use of an array would be faster (depending on the CPU cache, of course). Just my opinion. ----------------- |
Chris Katko
Member #1,881
January 2002
|
Fixed-point and custom formats are actually very common in AAA studios. For one reason, the self-wrapping feature Niunio mentioned. You'll see SINGLE BYTE, unsigned floats in places where memory bandwidth is key and the underlying DATA being represented can both survive the granularity, as well as take advantage of domains smaller than all real numbers, such as unsigned. For an easy example. If your number can't be outside 0.0 to 1.0, a float or double wastes bits. (In areas where that data is a bottleneck.) That said, >90% of us don't need them for an indie game where computing resources usually far exceed our ability to create content that exploits them. (Barring stupid decisions like using algorithms of unnecessarily high time-complexity.) -----sig: |
Elias
Member #358
May 2000
|
float is actually very dangerous if you don't make sure to wrap it back into the 2pi range. It takes only a few 1000 rotations in the same direction otherwise before accuracy drops down to full angles. It's even worse with x/y positions... if your level is big enough that you can reach around pixel 200,000 then you're down to 2 decimal points, i.e.: float x = 300000; float y = x + 0.01; if (y > x) // false if (y == x) // true It will probably mean while your physics work perfectly fine close to the center you get very strange behavior farther away. Of course simply using double instead of float should fix that in most cases -- |
Chris Katko
Member #1,881
January 2002
|
Likewise, Fun fact: At 16,777,216, floats no longer have the resolution to increment by one. The code: for(float i = 0; i <= 16,777,216 + 1; i++) //16,777,216 + 1 = 16,777,216 (rounded down) will never terminate. So you should never rely on a float for an integer value that exceeds that value. And likewise, for decimal numbers that you care about that exceed the precision. And on the flip side, you CAN use a float for things like array indices if you need to. (Using float x for position in space, and when you land on a space station, you x = the map array index.) But it's still probably a bad idea unless you NEED that optimization... Elias said: It will probably mean while your physics work perfectly fine close to the center you get very strange behavior farther away I had that happen to me, even with doubles. I was making a 2-D KSP using actual universe scale dimensions. It would fall apart adding a small distance every frame (velocity) at huge distances. fixed point are great for that in the sense that their precision never changes where ever you are in the grid. I still wonder sometimes why CPU's don't natively support fixed operations. -----sig: |
amarillion
Member #940
January 2001
|
All good points. But I think I should at least update the article to explain the pros and cons. And for a beginner tutorial, fixed vs float might be too much of a distraction of the real point, which is about trigonometry. Maybe I should write a separate article on fixed vs float. -- |
Niunio
Member #1,975
March 2002
|
amarillion said: All good points. But I think I should at least update the article to explain the pros and cons. And for a beginner tutorial, fixed vs float might be too much of a distraction of the real point, which is about trigonometry. Maybe I should write a separate article on fixed vs float. Agree. ----------------- |
Chris Katko
Member #1,881
January 2002
|
Oh yeah, I don't think it's be great for the article. I was just talking about some cool stuff I learned about fixed/float. -----sig: |
Felix-The-Ghost
Member #9,729
April 2008
|
I'm bookmarking this thread. I had used these functions in A4 for turrets that always face the player, but now that I am reviewing this material again with much more experience I can appreciate better the ingenuity of using integers in lieu of floats when granularity is low. I feel like this would be great in saving data or networking, so you can send 1-2 bytes of data for a float instead of 4+ bytes. |
|