|
Raycasting problems |
aybabtu
Member #2,891
November 2002
|
I just read part of the "Sin & Cos: The Programmer's Pals!" tutorial by amarillion. I previously didn't know how to apply these functions to movement, but I do now!! (BTW: Great tutorial!!!) Well, after experimenting with them and making a simple "game" to test them out, I've decided to do a raycasting engine. I just want it simple like Wolfenstein 3D's engine. I've got some code "working", but there's a problem with the displaying of the level, it's really blocky, and it doesn't look like walls or anything, just big straight blocks. Ther is some difference in heights and such, but I can't figure out where corners are and stuff. Like I said, I'm new to using angles and sin&cos and stuff like that in my programs, so maybe that's one of the problems, too. Here's the display/casting code:
If that's not enough to find the problem, here's the whole program's code:
I'm almost certain it's a problem with the display or the casting function, but I'm not sure. |
Thomas Harte
Member #33
April 2000
|
Quote: the_x+=(int)cos(a); This is equivalent to: if(a == M_PI) x--; i.e. cos(a) only varies from -1 to +1, so the (int) essentially throws all the information away. [My site] [Tetrominoes] |
aybabtu
Member #2,891
November 2002
|
So do I need to have the x&y floats or fixeds or doubles? As I said all the movement and angle code worked in my other "game" test. Well, why would it matter? You can't put a point at a non-integer coordinate anyway, so I don't need that extra information, right? |
Goodbytes
Member #448
June 2000
|
Yes, you have to have the x&y float or fixed or double. Trust us, it matters. Think about it. cos(a) and sin(a) are going to be the same thing every time you calculate them. So, if your x increment for a particular ray is either -1, 0, or 1 all the time, and your y increment is either -1, 0, or 1 all the time, any rays you cast will automagically be clamped to 45 degree increments, which is definitely not what you want. P.S. You can precalculate the values of cos(a) and sin(a) instead of calling those functions repeatedly in your raycasting loop. It will make for a huge speed increase. |
aybabtu
Member #2,891
November 2002
|
So instead of haveing it shoot a ray out at 1 degree increments, it shoots them out a t 45 degree increments? That explains the blockyness of the output. So, I need to have the_x & the_y be floats, to get an accurate reading, right? Quote: You can precalculate the values of cos(a) and sin(a) instead of calling those functions repeatedly in your raycasting loop. It will make for a huge speed increase. I think I'll probly do this later, but for now, I'll just call it every time. I don't think its a big problem, cuz it runs fine on my old 586 computer. But, how is this done? I'm guessing just by filling something like float sin_table[85] or something, right? How much would I increment it for each part of the table? 1 or .1 or somthing? float sin_table[85]; for(int i=0;i<85;i++) { sin_table<i>=sin(i); } ? |
amarillion
Member #940
January 2001
|
the allegro functions fixsin and fixcos are already implemented using tables. Trust me, don't worry about optimization too much at first. Just get it working first, optimize later. -- |
ROSTheFuture
Member #2,775
September 2002
|
If you aren't getting any compile errors or runtime errors, i would say try a different, more complicated level. Probably whats happening is that the level is too simple, so naturally all you see is blocks. [edit] or not I really need to speed up my typing [/edit] Also what you might want to do is modify your code a bit so that walls can be different colour so that you can differentiate them. All that would take to do that would be to make your level array with different values that one, then you would change your ray casting code, and add another array parallel to 'view' which contains colour information:
just a suggestion. I don't know how far you intended to go with this.
-------- |
aybabtu
Member #2,891
November 2002
|
Image... As you can see, I've got it displaying right (changed the_x&y to floats), and it has a primitive form of depth shading! (The line in the middle is just to show the direction you're facing.) Also, you'll notice that it's got that nasty curved effect. How do I fix that? |
amarillion
Member #940
January 2001
|
Well, it is because raycasting doesn't work the way you think it works. It's hard to explain but you are in the wrong line of thinking. Try to see if you can find other raycasting tutorials. -- |
aybabtu
Member #2,891
November 2002
|
What's wrong with my line of thinking? Well, I've read another nice tutorial, and it says to fix the fishbowl effect, multiply the distance by cos(BETA) and beta is -30 (since I'm using the 255 degree circle, -21) for the left-most ray, and 30 (21) for the rightmost ray. I've solved this by doing distance*=cos(0-a); and it works. But, the angle should be between -21 and 21, but in reality, it can be anywhere between two numbers that are 42 different. SO, the effect I get is that when you turn 90 degrees (64 allegro-degrees) you get an incorrect reading! I think I'm stuck... |
Zaphos
Member #1,468
August 2001
|
aybabtu: Try changing 0 to the direction you're facing so that 0-a is between -21 and 21 regardless of how offset a is. e.g. if you turn 64 alleg-degs then you multiply distance by cos(64-a). Anyway, you should really try to get a grasp of the trig underlying what you're doing ... I think this calls for some smiling art: :-X == Whitespace The size of each bit of wall you draw is determined by the distance parallel to your line of sight that you'd need to walk to get to that wall -- perpendicular distance should not "shrink" the wall. How do we find that parallel distance? Well, if we look at the diagram above as a right-triangle, we see that we already have the length of the hypotenuse and the angle, and we can apply simple trig to find that that parallel distance is hypot*cos(angle) ... exactly the formula you're using.
|
Niunio
Member #1,975
March 2002
|
Here you have a raycast tutorial. It explains what's happening with that nasty curved effect and how to prevent it. Let me know when you release this project, please;). ----------------- |
amarillion
Member #940
January 2001
|
You see? That other tutorial is much better. You see, mine is not really intended as a raycasting tutorial, just a trig primer. -- |
aybabtu
Member #2,891
November 2002
|
amarillion: Yeah, I know. I've learned a little trigonometry in my geometry class at school, but we never learned really how to apply it to a computer game ! But, even though your tutorial is not for raycasting in particular, it was what I was missing to at least get started. After all, at least it's farther than I've ever gotten before. Niuino: Yeah! That tutorial's great, it's the same one you linked to in my other raycasting thread. I'm trying to use that method, but it's not working very well. I've gotten rid of some of the fishbowl, but in some parts, it's actually reversed! Like, I'll look at a flat wall, and it will have slight curvature to it, but instead of the middle being larger, the ends are. Here's the code I'm using to "fix" the distance problem: void do_raycasting() { float angle=pa-21; for(int i=0;i<42;i++) { view<i>=cos(i-21)*cast_ray(px,py,angle); angle++; } } This is what the tutorial says to do, right? The Tutorial said:
Thus to remove the viewing distortion, the resulting distance obtained from equations in Figure 17 must be multiplied by cos(BETA); where BETA is the angle of the ray that is being cast relative to the viewing angle. On the figure above, the viewing angle (ALPHA) is 90 degrees because the player is facing straight upward. Because we have 60 degrees field of view, BETA is 30 degrees for the leftmost ray and it is -30 degrees for the rightmost ray.
Of course, I changed 30 degrees into it's equivalent (about 21) in allegro degrees. Quote: Let me know when you release this project, please. Hmmm...I don't know even what I'm going to do with it yet! Probly a shooter like Wolfenstein, but between then and now, I've gotta get it working right! My next task is texture mapping. goes to the tutorial... |
Niunio
Member #1,975
March 2002
|
view<i> = cos (i-21) * cast_ray (px, py, angle); That cos function, doesn't it get the angle in radians? But you're using binary angles (0-255), don't you? Try with fixcos or fcos, and do not forget to translate it to fixed point. ----------------- |
Trezker
Member #1,739
December 2001
|
If you go to http://www.neozones.com and search a bit, you'll find some raycasting tutorials, they're in qbasic but I've made some advancements in c++. I attach my work and the original tutorials. |
Zaphos
Member #1,468
August 2001
|
Well, given that the cos has worked as well as it has, I'd guess that aybabtu hasn't #included math.h and the allegro functions are stepping in automatically, so that isn't the problem. However it would be a good idea to follow Niunio's advice all the same so that the eventual possible #include math.h won't break all your math. Anyway, the function looks right too me, so if Niunio isn't on to something then all I can guess is that there's something minorly wrong with "cast_ray()" ... perhaps the your steps are too big so the granularity is causing some small error in the drawing? How significant is the distortion?
|
aybabtu
Member #2,891
November 2002
|
Here's a pic with my crappy texture mapping and distortion correction working: As you can see, there's a few problems in the texture mapping, but it's ok for now. But, you'll notice the distortion in that cube. Niuino: As Zaphos said, I don't have math.h included, so allegro's helping me out! I could change them to radians, but this works. Later when I get all the bugs worked out of it, I'll make all the code "the way it should be".;D Trezker: Yeah! Even if they're in QBASIC, I know enough (not a lot still) to see how they work, and implement that code in C. Zaphos: Um...as for the distortion...it sorta looks like a reverse-fishbowl effect. Instead of the distortion being on the sides, it's in the middle. And, I think I may know the reason...maybe. Maybe because 30 degrees isn't exactly equal to 21 allegro degrees, but 21.25 rather. So, the view is being cut off by half a degree, possibly screwing it up. Actually, it shouldn't do that kind of damage, just make the screen smaller...oh well. 1#include <allegro.h>
2
3#define TILE 64
4
5int level[9][9]={
6{1,1,1,1,1,1,1,1,1},
7{1,0,0,0,0,0,0,0,1},
8{1,0,1,0,1,0,1,0,1},
9{1,0,0,0,0,0,0,0,1},
10{1,0,1,0,1,0,1,0,1},
11{1,0,0,0,0,0,0,0,1},
12{1,0,1,0,1,0,1,0,1},
13{1,0,0,0,0,0,0,0,1},
14{1,1,1,1,1,1,1,1,1}};
15
16int view[42];
17int texture_map[42];
18
19int px=65;
20int py=65;
21int p_old_x=65;
22int p_old_y=65;
23int ps=5;
24float pa=32.0;
25
26bool fishbowl=false;
27bool textures_off=false;
28
29BITMAP*front;
30BITMAP*backdrop;
31BITMAP*texture;
32
33float cast_ray(int x,int y,float a);
34void draw_view();
35void do_raycasting();
36bool collisions();
37void my_draw_circle(BITMAP*b,int x,int y,int radius,int color);
38
39int main()
40{
41 allegro_init();
42 install_keyboard();
43 set_color_depth(24);
44 set_gfx_mode(GFX_AUTODETECT,320,200,0,0);
45 front=create_bitmap(320,200);
46 backdrop=create_bitmap(320,200);
47 for(int i=0;i<100;i++) hline(backdrop,0,i,320,makecol(200,200,255-i));
48 rectfill(backdrop,0,100,320,200,makecol(0,100,0));
49 text_mode(-1);
50 texture=load_bitmap("texture.bmp",NULL);
51 while(!key[KEY_ESC])
52 {
53 clear(front);
54 blit(backdrop,front,0,0,0,0,320,200);
55 do_raycasting();
56 draw_view();
57 if(key[KEY_F]) fishbowl=true;
58 if(key[KEY_G]) fishbowl=false;
59 if(key[KEY_T]) textures_off=false;
60 if(key[KEY_R]) textures_off=true;
61 if(key[KEY_LEFT]) pa-=2;
62 if(key[KEY_RIGHT]) pa+=2;
63 if(key[KEY_UP])
64 {
65 px+=(int)(ps*cos(pa));
66 py+=(int)(ps*sin(pa));
67 }
68 if(key[KEY_DOWN])
69 {
70 px-=(int)(ps*cos(pa));
71 py-=(int)(ps*sin(pa));
72 }
73 if(collisions()==true)
74 {
75 px=p_old_x;
76 py=p_old_y;
77 }
78 else
79 {
80 p_old_x=px;
81 p_old_y=py;
82 }
83 if(pa<0) pa+=255;
84 if(pa>255) pa-=255;
85 line(front,160,100,160+(25*cos(pa)),100+(25*sin(pa)),makecol(0,0,255));
86 line(front,160,100,160+(25*cos(pa-21)),100+(25*sin(pa-21)),makecol(255,255,0));
87 line(front,160,100,160+(25*cos(pa+21)),100+(25*sin(pa+21)),makecol(255,255,0));
88 my_draw_circle(front,160,100,25,makecol(255,0,0));
89 textout_centre(front,font,"N",160,65,makecol(0,0,255));
90 vsync();
91 blit(front,screen,0,0,0,0,320,200);
92 if(key[KEY_S]) save_bitmap("wolf.pcx",front,NULL);
93 }
94 destroy_bitmap(front);
95 return 0;
96}
97
98int map_texture(int x,int y,float a)
99{
100 float the_x=x;
101 float the_y=y;
102 bool ended_on_y=false;
103 while(level[(int)the_y/TILE][(int)the_x/TILE]==0)
104 {
105 the_x+=(float)cos(a);
106 if(level[(int)the_y/TILE][(int)the_x/TILE]!=0)
107 {
108 ended_on_y=true;
109 break;
110 }
111 the_y+=(float)sin(a);
112 if(level[(int)the_y/TILE][(int)the_x/TILE]!=0)
113 {
114 ended_on_y=false;
115 break;
116 }
117 }
118 if(ended_on_y==true) return (int)((int)the_y%TILE);
119 if(ended_on_y==false) return (int)((int)the_x%TILE);
120}
121
122float cast_ray(int x,int y,float a)
123{
124 float distance=0;
125 float the_x=x;
126 float the_y=y;
127 while(level[(int)the_y/TILE][(int)the_x/TILE]==0)
128 {
129 the_x+=(float)cos(a);
130 the_y+=(float)sin(a);
131 distance++;
132 }
133 return distance;
134}
135
136void do_raycasting()
137{
138 float angle=pa-21;
139 for(int i=0;i<42;i++)
140 {
141 if(fishbowl==false) view<i>=(int)(cos(i-21)*cast_ray(px,py,angle));
142 if(fishbowl==true) view<i>=(int)cast_ray(px,py,angle);
143 texture_map<i>=map_texture(px,py,angle);
144 angle++;
145 }
146}
147
148void draw_view()
149{
150 int tall=0;
151 int distance_from_top=0;
152 int color=255;
153 for(int i=0;i<42;i++)
154 {
155 if(view<i><200)
156 {
157 color=255-(view<i>);
158 tall=200-view<i>;
159 distance_from_top=(200-tall)/2;
160 if(textures_off==true) rectfill(front,(int)(i*(320/42.5))+1,distance_from_top,(int)(((320/42.5)*i)+(320/42.5)),distance_from_top+tall,makecol(color,color,color));
161 if(textures_off==false) stretch_blit(texture,front,texture_map<i>,0,(int)(320/42.5),64,(int)(i*(320/42.5)),distance_from_top,((int)(320/42.5))+1,tall);
162 }
163 }
164}
165
166void my_draw_circle(BITMAP*b,int x,int y,int radius,int color)
167{
168 float angle=0.0;
169 while(angle<255)
170 {
171 putpixel(b,x+(radius*cos(angle)),y+(radius*sin(angle)),color);
172 angle+=0.1;
173 }
174}
175
176bool collisions()
177{
178 if(level[py/TILE][px/TILE]==0) return false;
179 else return true;
180}
(Note: if you want to compile this or anything, you need a 64x64 texture bitmap for the walls...duh!:P) EDIT: In the files that Trezker attached, I found this: 1'----- CONSTRUCT THE VIEWFIX TABLE
2'----- NOTICE THAT z% GOES FROM -31 TO 32 BECAUSE OUR RAYCAST GOES FROM -31 TO
3'----- 32
4FOR z% = -31 TO 32
5 ViewFix(z%) = 1 / COS(z% * PI / 180) '----- HERE'S THE FORMULA FOR YOUR
6 '----- VIEWFIX TABLE
7NEXT z%
8
9//Later in the code...
10'***** This is where we cast out our ray. We add yy% by the rise, and xx%
11'***** by then run. But I subtract it so that angle 0 is infront of you.
12DO
13 'xx% = xx% - SinTable(a%) '----- IT USED TO BE
14 'yy% = yy% - CosTable(a%) '----- THIS, BUT
15 xx% = xx% - SinTable(a%) * ViewFix(z%) '----- NOW IT'S
16 yy% = yy% - CosTable(a%) * ViewFix(z%) '----- THIS, WE MULTIPLIED OUR STEP
17 '----- TABLES BY OUR VIEWFIX TABLE
18 '----- ACCORDING TO z%
So, I need to construct a table like ViewFix, then multiply the reading from cast_ray by that value that corresponds to the angle, right? Or is that exacly what I'm doing...? |
Zaphos
Member #1,468
August 2001
|
I would actually suggest implementing the faster, more accurate method of detecting walls, explained on this page of Nuino's linked tutorial, as I think the distortion may be caused by your current distance calculations (which are only of mediocre accuracy ...)
|
aybabtu
Member #2,891
November 2002
|
Zaphos: Yup. I'm working on making it so that the do_raycasting fuction shoots out 320 rays, 1 for each pixel on the screen, which will eliminate all bugs in the texture mapping, and possibly fix some of the distortion. It shouldn't take too long... BTW: That qbasic fix for the distortion didn't hardly help at all...:-/ just a little. Oh well... |
Zaphos
Member #1,468
August 2001
|
Quote: I'm working on making it so that the do_raycasting fuction shoots out 320 rays I wasn't talking about that, but rather the way in which you calculate the distance, but then again for a large enough value of TILE using the method in that tutorial will probably be more of a speed boost than an accuracy one.
|
aybabtu
Member #2,891
November 2002
|
Perfect texture mapping, courtesy of 320 rays being cast: But, 320 rays is A LOT of math per frame!! If I hold down an arrow key, it's at least .75 seconds between frames/moves. How do I display the exact fps? I belive the floating point numbers are causing part of the slowdown (but fixed looks like it'd be too hard to convert to from it's current state...), but another problem is that it does a lot of math! That optimization that Zaphos showed me would be great, but I'm afraid I don't exactly understand it. I'll look at it some more, but any help would be greatly appreciated! I modified the code a lot: 1#include <allegro.h>
2
3#define TILE 64
4
5int level[9][9]={
6{1,1,1,1,1,1,1,1,1},
7{1,0,0,0,0,0,0,0,1},
8{1,0,1,0,1,0,1,0,1},
9{1,0,0,0,0,0,0,0,1},
10{1,0,1,0,1,0,1,0,1},
11{1,0,0,0,0,0,0,0,1},
12{1,0,1,0,1,0,1,0,1},
13{1,0,0,0,0,0,0,0,1},
14{1,1,1,1,1,1,1,1,1}};
15
16int view[320];
17int texture_map[320];
18
19int px=65;
20int py=65;
21int p_old_x=65;
22int p_old_y=65;
23int ps=5;
24float pa=32.0;
25
26bool fishbowl=false;
27bool textures_off=false;
28
29BITMAP*front;
30BITMAP*backdrop;
31BITMAP*texture;
32
33float cast_ray(int x,int y,float a);
34void draw_view();
35void do_raycasting();
36bool collisions();
37void my_draw_circle(BITMAP*b,int x,int y,int radius,int color);
38
39int main()
40{
41 allegro_init();
42 install_keyboard();
43 set_color_depth(24);
44 set_gfx_mode(GFX_AUTODETECT,320,200,0,0);
45 front=create_bitmap(320,200);
46 backdrop=create_bitmap(320,200);
47 for(int i=0;i<100;i++) hline(backdrop,0,i,320,makecol(200,200,255-i));
48 rectfill(backdrop,0,100,320,200,makecol(0,100,0));
49 text_mode(-1);
50 texture=load_bitmap("texture.bmp",NULL);
51 while(!key[KEY_ESC])
52 {
53 clear(front);
54 blit(backdrop,front,0,0,0,0,320,200);
55 do_raycasting();
56 draw_view();
57 if(key[KEY_F]) fishbowl=true;
58 if(key[KEY_G]) fishbowl=false;
59 if(key[KEY_T]) textures_off=false;
60 if(key[KEY_R]) textures_off=true;
61 if(key[KEY_A])
62 {
63 px+=(int)(ps*cos(pa-64));
64 py+=(int)(ps*sin(pa-64));
65 }
66 if(key[KEY_D])
67 {
68 px-=(int)(ps*cos(pa-64));
69 py-=(int)(ps*sin(pa-64));
70 }
71 if(key[KEY_LEFT]) pa-=2;
72 if(key[KEY_RIGHT]) pa+=2;
73 if(key[KEY_UP])
74 {
75 px+=(int)(ps*cos(pa));
76 py+=(int)(ps*sin(pa));
77 }
78 if(key[KEY_DOWN])
79 {
80 px-=(int)(ps*cos(pa));
81 py-=(int)(ps*sin(pa));
82 }
83 if(collisions()==true)
84 {
85 px=p_old_x;
86 py=p_old_y;
87 }
88 else
89 {
90 p_old_x=px;
91 p_old_y=py;
92 }
93 if(pa<0) pa+=255;
94 if(pa>255) pa-=255;
95 line(front,160,100,160+(25*cos(pa)),100+(25*sin(pa)),makecol(0,0,255));
96 line(front,160,100,160+(25*cos(pa-21)),100+(25*sin(pa-21)),makecol(255,255,0));
97 line(front,160,100,160+(25*cos(pa+21)),100+(25*sin(pa+21)),makecol(255,255,0));
98 my_draw_circle(front,160,100,25,makecol(255,0,0));
99 textout_centre(front,font,"N",160,65,makecol(0,0,255));
100 vsync();
101 blit(front,screen,0,0,0,0,320,200);
102 if(key[KEY_S]) save_bitmap("wolf.pcx",front,NULL);
103 }
104 destroy_bitmap(front);
105 destroy_bitmap(texture);
106 destroy_bitmap(backdrop);
107 return 0;
108}
109
110int map_texture(int x,int y,float a)
111{
112 float distance=0.0;
113 float the_x=x;
114 float the_y=y;
115 bool ended_on_y=false;
116 while(level[(int)the_y/TILE][(int)the_x/TILE]==0)
117 {
118 the_x+=(float)cos(a);
119 if(level[(int)the_y/TILE][(int)the_x/TILE]!=0)
120 {
121 ended_on_y=true;
122 break;
123 }
124 the_y+=(float)sin(a);
125 if(level[(int)the_y/TILE][(int)the_x/TILE]!=0)
126 {
127 ended_on_y=false;
128 break;
129 }
130 distance++;
131 }
132 if(ended_on_y==true) return (int)((int)the_y%TILE);
133 if(ended_on_y==false) return (int)((int)the_x%TILE);
134}
135
136float cast_ray(int x,int y,float a)
137{
138 float distance=0.0;
139 float the_x=x;
140 float the_y=y;
141 while(level[(int)the_y/TILE][(int)the_x/TILE]==0)
142 {
143 the_x+=(float)cos(a);
144 the_y+=(float)sin(a);
145 distance++;
146 }
147 return distance;
148}
149
150void do_raycasting()
151{
152 float angle=pa-21.25;
153 for(int i=0;i<320;i++)
154 {
155 if(fishbowl==false) view<i>=(int)(cos((i/7.529411765)-21.25)*cast_ray(px,py,angle));
156 if(fishbowl==true) view<i>=(int)cast_ray(px,py,angle);
157 texture_map<i>=map_texture(px,py,angle);
158 angle+=0.13125;
159 }
160}
161
162void draw_view()
163{
164 int tall=0;
165 int distance_from_top=0;
166 int color=255;
167 for(int i=0;i<320;i++)
168 {
169 if(view<i><200)
170 {
171 color=255-(view<i>);
172 tall=200-view<i>;
173 distance_from_top=(200-tall)/2;
174 if(textures_off==true) rectfill(front,i+1,distance_from_top,i+2,distance_from_top+tall,makecol(color,color,color));
175 if(textures_off==false) stretch_blit(texture,front,texture_map<i>,0,1,64,i,distance_from_top,1,tall);
176 }
177 }
178}
179
180void my_draw_circle(BITMAP*b,int x,int y,int radius,int color)
181{
182 float angle=0.0;
183 while(angle<255)
184 {
185 putpixel(b,x+(radius*cos(angle)),y+(radius*sin(angle)),color);
186 angle+=0.1;
187 }
188}
189
190bool collisions()
191{
192 if(level[py/TILE][px/TILE]==0) return false;
193 else return true;
194}
Ack...::) EDIT: I have a theory about the distortion. It's compensating too much. Like I said, before, the middle was bulged out. Now, the middle is right, but the sides are bulged. This would lead me to belive that it's compensating too much for the distortion! Maybe: int distance=(int)cast_ray(px,py,angle); int compensated=distance*cos((i/7.529411765)-21.25); int nice_distance=(compensated+distance)/2; I know, this would give me the average. But, from what it looks lke, this would produce straight walls! I dunno, I'll try it...probly won't be the solution, though. But, hey! Anything's worth a try! |
23yrold3yrold
Member #1,134
March 2001
|
Heh; that distortion is funny I stole your code, so I figure I give you a hand. FPS is pretty low for me too; less than 10fps. I notice it gets pretty fast when a wall is right in your face though, so maybe your engine is wasting its time finding far walls? Comment your code some and maybe I could understand it I'm going to play with it some; I'm bored ... EDIT: The tutorial Niunio linked to states (correctly) that you can speed up calculations using bitshifts. A quick Find reveals you aren't doing any bitshifting in your engine; only multiplication and division ... I just tried it, and not much difference ... EDIT2: I found a major problem; in map_texture(), these two lines: Do you have any clue how you're inching along there? Those are small values; try this instead: Not sure how correct that is, but it's a nice healthy speed boost ... EDIT3: Eek! cast_ray() is pretty bodgy; I gotta figure out how to rewrite that (see Niunio's link again). -- |
aybabtu
Member #2,891
November 2002
|
Quote: Do you have any clue how you're inching along there? Those are small values; try this instead:
Okay! EDIT: Commented code for 23: 1#include <allegro.h>
2
3#define TILE 64 //Block (tile) width,height,depth
4
5int level[9][9]={ //A level
6{1,1,1,1,1,1,1,1,1}, //A 1 is a wall, 0 is floor
7{1,0,0,0,0,0,0,0,1},
8{1,0,1,0,1,0,1,0,1},
9{1,0,0,0,0,0,0,0,1},
10{1,0,1,0,1,0,1,0,1},
11{1,0,0,0,0,0,0,0,1},
12{1,0,1,0,1,0,1,0,1},
13{1,0,0,0,0,0,0,0,1},
14{1,1,1,1,1,1,1,1,1}};
15
16int view[320]; //The wall slice distances
17int texture_map[320]; //X offests for texture map
18
19int px=65; //Player x
20int py=65; //Player y
21int p_old_x=65; //Player's old x (collision detection)
22int p_old_y=65; //Old y
23int ps=5; //Speed
24float pa=32.0; //Angle
25
26bool fishbowl=false; //So I can switch on/off distortion correction
27bool textures_off=false; //Same, but for textures
28
29BITMAP*front; //The buffer that's blit to screen
30BITMAP*backdrop; //Background image (sky and grass)
31BITMAP*texture; //Wall texture (64x64)
32
33float cast_ray(int x,int y,float a); //Casts a ray at a given angle, then returns distance
34void draw_view(); //Draws the view to screen according to view[320]
35void do_raycasting(); //Handles the raycasting
36bool collisions(); //Handles collisions
37void my_draw_circle(BITMAP*b,int x,int y,int radius,int color); //I wanted to make my own circle drawing function!!
38
39int main()
40{
41 allegro_init();
42 install_keyboard();
43 set_color_depth(24);
44 set_gfx_mode(GFX_AUTODETECT,320,200,0,0);
45 front=create_bitmap(320,200);
46 backdrop=create_bitmap(320,200);
47 //Next few lines draw backdrop
48 for(int i=0;i<100;i++) hline(backdrop,0,i,320,makecol(200,200,255-i));
49 rectfill(backdrop,0,100,320,200,makecol(0,100,0));
50 text_mode(-1);
51 //Loads texture.bmp
52 texture=load_bitmap("texture.bmp",NULL);
53 while(!key[KEY_ESC])
54 {
55 clear(front); //Clear the buffer
56 blit(backdrop,front,0,0,0,0,320,200); //draw bg
57 do_raycasting(); //Cast the rays
58 draw_view(); //Draw the view of the level
59 if(key[KEY_F]) fishbowl=true; //Switch stuff on/off
60 if(key[KEY_G]) fishbowl=false;
61 if(key[KEY_T]) textures_off=false;
62 if(key[KEY_R]) textures_off=true;
63 //Next six keys are movement
64 if(key[KEY_A]) //Strafe left
65 {
66 px+=(int)(ps*cos(pa-64));
67 py+=(int)(ps*sin(pa-64));
68 }
69 if(key[KEY_D]) //Strafe right
70 {
71 px-=(int)(ps*cos(pa-64));
72 py-=(int)(ps*sin(pa-64));
73 }
74 if(key[KEY_LEFT]) pa-=2; //Turn left
75 if(key[KEY_RIGHT]) pa+=2; //Turn right
76 if(key[KEY_UP]) //Move foward
77 {
78 px+=(int)(ps*cos(pa));
79 py+=(int)(ps*sin(pa));
80 }
81 if(key[KEY_DOWN]) //Move backward
82 {
83 px-=(int)(ps*cos(pa));
84 py-=(int)(ps*sin(pa));
85 }
86 if(collisions()==true) //check for collisions
87 {
88 px=p_old_x;
89 py=p_old_y;
90 }
91 else
92 {
93 p_old_x=px;
94 p_old_y=py;
95 }
96 if(pa<0) pa+=255; //Keep pa between 0-255
97 if(pa>255) pa-=255;
98 //Next few lines draw that compass thing
99 //Blue line is your angle, yellow is your fov
100 line(front,160,100,160+(25*cos(pa)),100+(25*sin(pa)),makecol(0,0,255));
101 line(front,160,100,160+(25*cos(pa-21)),100+(25*sin(pa-21)),makecol(255,255,0));
102 line(front,160,100,160+(25*cos(pa+21)),100+(25*sin(pa+21)),makecol(255,255,0));
103 my_draw_circle(front,160,100,25,makecol(255,0,0));
104 textout_centre(front,font,"N",160,65,makecol(0,0,255));
105 vsync(); //Duh...
106 blit(front,screen,0,0,0,0,320,200); //Draw buffer to screen
107 if(key[KEY_S]) save_bitmap ("wolf.pcx",front,NULL); //Save screenshots
108 }
109 destroy_bitmap(front);
110 destroy_bitmap(texture);
111 destroy_bitmap(backdrop);
112 return 0;
113}
114
115//Gets the X offset for the texture
116int map_texture(int x,int y,float a)
117{
118 float distance=0.0;
119 float the_x=x; //X of the ray's position
120 float the_y=y; //Y of the ray's postition
121 bool ended_on_y=false; //Check that it ends on a horizontal face or a vertical face of a block
122 while(level[(int)the_y/TILE][(int)the_x/TILE]==0) //Keeps going until a wall
123 {
124 the_x+=(float)cos(a); //Increase x
125 if(level[(int)the_y/TILE][(int)the_x/TILE]!=0) //Check to see if this movement caused a collision
126 {
127 ended_on_y=true; //If it did, it ended on a vertical face
128 break; // End the loop
129 }
130 the_y+=(float)sin(a); //If it's still going, increase y
131 if(level[(int)the_y/TILE][(int)the_x/TILE]!=0) //Once again, check collisions
132 {
133 ended_on_y=false;
134 break;
135 }
136 distance++; //Not used yet, but will be so if a ray doesn't hit a wall, it'll stop still
137 }
138 if(ended_on_y==true) return (int)((int)the_y%TILE); //The equation for finding the offset is different for a vertical face than a horizontal
139 if(ended_on_y==false) return (int)((int)the_x%TILE);
140}
141
142//Casts the ray, and returns the distance
143float cast_ray(int x,int y,float a)
144{
145 float distance=0.0;
146 float the_x=x; // Ray x
147 float the_y=y; // Ray y
148 while(level[(int)the_y/TILE][(int)the_x/TILE]==0) //Keep going until a wall
149 {
150 the_x+=(float)cos(a); //Increase x
151 the_y+=(float)sin(a); //Increase y
152 distance++; //Increase distance
153 }
154 return distance;
155}
156
157void do_raycasting()
158{
159 float angle=pa-21.25; //Start at the equivalent of -30 degrees
160 for(int i=0;i<320;i++) //Fill the view[]
161 {
162 //For some reason, this distortion correction doesn't help much...
163 // i/7.5... gets the angle i should be
164 // -21.25 because it'll be between 0-42.5
165 if(fishbowl==false) view<i>=(int)(cos((i/7.529411765)-21.25)*cast_ray(px,py,angle));
166 if(fishbowl==true) view<i>=(int)cast_ray(px,py,angle);
167 texture_map<i>=map_texture(px,py,angle); //Get texture
168 angle+=0.13125; //Add the angle by 42/360
169 }
170}
171
172//Draws the wall slices
173void draw_view()
174{
175 int tall=0; //How tall a slice is
176 int distance_from_top=0; //For it to be centered
177 int color=255; //For depth shading (no textures)
178 for(int i=0;i<320;i++)
179 {
180 if(view<i><200) //I need to fix this so I can display walls that are farther away
181 {
182 color=255-(view<i>); //Gets the color
183 tall=200-view<i>; //Gets the height
184 distance_from_top=(200-tall)/2; //Centers it
185 //If textures are off, it just uses rectfill
186 //These drawing functions are easy enough to understand...
187 if(textures_off==true) rectfill (front,i+1,distance_from_top,i+2,distance_from_top+tall,makecol(color,color,color));
188 if(textures_off==false) stretch_blit(texture,front,texture_map<i>,0,1,64,i,distance_from_top,1,tall);
189 }
190 }
191}
192
193//Custom circle drawing function
194//Note that this doesn't contribute to the slowdown....
195void my_draw_circle(BITMAP*b,int x,int y,int radius,int color)
196{
197 float angle=0.0;
198 while(angle<255)
199 {
200 putpixel(b,x+(radius*cos(angle)),y+(radius*sin(angle)),color);
201 angle+=0.1;
202 }
203}
204
205bool collisions()
206{
207 if(level[py/TILE][px/TILE]==0) return false;
208 else return true;
209}
|
23yrold3yrold
Member #1,134
March 2001
|
It worked well, actually. But I've been working on a "proper" version using the math on that site (thread bump is t3h win, btw) and here's my best effort so far. In the plus column, it's blazingly fast; in the minus column I think I'm screwing up the math and I'm too stupid to figure out where It looks like a mess if you try it, but actually about half the walls are being drawn correctly and the other half aren't. The program also tends to crash on me if the angle I pass to tan() is undesirably too close to a multiple of 90 degrees, which has a bad hack to fix. Anyone want to give us some pointers here? 1
2#include <math.h>
3#include <allegro.h>
4
5#define TILE 64
6#define TILESHIFT 6
7#define SCREENWIDTH 320
8#define SCREENHEIGHT 200
9#define M_PI 3.1415926535897932
10
11int level[9][9]={
12{1,1,1,1,1,1,1,1,1},
13{1,0,0,0,0,0,0,0,1},
14{1,0,1,0,1,0,1,0,1},
15{1,0,0,0,0,0,0,0,1},
16{1,0,1,0,1,0,1,0,1},
17{1,0,0,0,0,0,0,0,1},
18{1,0,1,0,1,0,1,0,1},
19{1,0,0,0,0,0,0,0,1},
20{1,1,1,1,1,1,1,1,1}};
21
22int view[320];
23int texture_map[320];
24int px = 65;
25int py = 65;
26int p_old_x = 65;
27int p_old_y = 65;
28int ps = 5; // player speed
29float pa = 1; // player angle
30bool fishbowl = false;
31bool textures_off = false;
32
33BITMAP* front;
34BITMAP* backdrop;
35BITMAP* texture;
36
37// float cast_ray(int x, int y, float a);
38float CastRay(int xpos, int ypos, double angle, int& t);
39void draw_view();
40void do_raycasting();
41bool collisions();
42void my_draw_circle(BITMAP *b, int x, int y, int radius, int color);
43
44int main()
45{
46 allegro_init();
47 install_keyboard();
48 set_color_depth(16);
49 set_gfx_mode(GFX_AUTODETECT, SCREENWIDTH, SCREENHEIGHT, 0, 0);
50 text_mode(-1);
51
52 front = create_bitmap(SCREENWIDTH, SCREENHEIGHT);
53 backdrop = create_bitmap(SCREENWIDTH, SCREENHEIGHT);
54
55 for(int i = 0 ; i < 100 ; ++i) hline(backdrop, 0, i, 320, makecol(200, 200, 255 - i));
56 rectfill(backdrop, 0, 100, SCREENWIDTH, SCREENHEIGHT, makecol(0,100,0));
57 texture = load_bitmap("texture.bmp",NULL);
58
59 while(!key[KEY_ESC])
60 {
61 clear(front);
62 blit(backdrop,front,0,0,0,0,SCREENWIDTH,SCREENHEIGHT);
63 do_raycasting();
64 draw_view();
65
66 if(key[KEY_T]) textures_off = false;
67 if(key[KEY_R]) textures_off = true;
68 if(key[KEY_A])
69 {
70 px += (int)(ps * cos(pa - M_PI / 2));
71 py += (int)(ps * sin(pa - M_PI / 2));
72 }
73 if(key[KEY_D])
74 {
75 px -= (int)(ps * cos(pa - M_PI / 2));
76 py -= (int)(ps * sin(pa - M_PI / 2));
77 }
78 if(key[KEY_LEFT]) pa -= 0.1;
79 if(key[KEY_RIGHT]) pa += 0.1;
80 if(key[KEY_UP])
81 {
82 px+=(int)(ps * cos(pa));
83 py+=(int)(ps * sin(pa));
84 }
85 if(key[KEY_DOWN])
86 {
87 px-=(int)(ps * cos(pa));
88 py-=(int)(ps * sin(pa));
89 }
90 if(collisions()==true)
91 {
92 px=p_old_x;
93 py=p_old_y;
94 }
95 else
96 {
97 p_old_x=px;
98 p_old_y=py;
99 }
100
101 if(pa < 0) pa += M_PI * 2;
102 if(pa > M_PI * 2) pa -= M_PI * 2;
103
104
105 // draw the little compass/circle
106 line(front,160,100,160+(25*cos(pa)),100+(25*sin(pa)),makecol(0,0,255));
107 line(front,160,100,160+(25*cos(pa-0.523598)),100+(25*sin(pa-0.523598)),makecol(255,255,0));
108 line(front,160,100,160+(25*cos(pa+0.523598)),100+(25*sin(pa+0.523598)),makecol(255,255,0));
109 my_draw_circle(front,160,100,25,makecol(255,0,0));
110 textout_centre(front,font,"N",160,65,makecol(0,0,255));
111
112
113 vsync();
114 blit(front,screen,0,0,0,0,SCREENWIDTH, SCREENHEIGHT);
115 if(key[KEY_S]) save_bitmap("wolf.pcx",front,NULL);
116 }
117
118 destroy_bitmap(front);
119 destroy_bitmap(texture);
120 destroy_bitmap(backdrop);
121
122 return 0;
123}
124END_OF_MAIN()
125
126// called once from int main()
127void do_raycasting()
128{
129 double angle = pa - 0.523598;
130
131 for(int i = 0 ; i < SCREENWIDTH ; ++i)
132 {
133 // get the distance of the ray
134 view<i> = (int)(cos(angle - pa) * CastRay(px, py, angle, texture_map<i>));
135
136 // increment the angle
137 angle += 1.04719 / SCREENWIDTH;
138 }
139}
140
141// called once from int main()
142void draw_view()
143{
144 int tall = 0;
145 int distance_from_top = 0;
146
147 for(int i = 0 ; i < SCREENWIDTH ; ++i)
148 {
149 tall = (int)(64.0 / view<i> * 277.0);
150 distance_from_top = (SCREENHEIGHT - tall) / 2;
151 stretch_blit(texture, front, texture_map<i>, 0, 1, 64, i, distance_from_top, 1, tall);
152 }
153}
154
155// trivial function called once fron int main()
156void my_draw_circle(BITMAP*b,int x,int y,int radius,int color)
157{
158 float angle = 0.0;
159
160 while(angle < 360)
161 {
162 putpixel(b, x + (radius * cos(angle)), y + (radius * sin(angle)), color);
163 angle += 0.1;
164 }
165}
166
167// called once from int main()
168bool collisions()
169{
170 // return (bool)level[py/TILE][px/TILE];
171 return (bool)level[px >> TILESHIFT][py >> TILESHIFT];
172}
173
174float CastRay(int xpos, int ypos, double angle, int& t) {
175 // initialize (X_Ycheck, X_Xcheck) with first vertical wall and
176 // (Y_Ycheck, Y_Xcheck) with the first horizontal one
177 float X_Ycheck, X_Xcheck, Y_Ycheck, Y_Xcheck;
178 float X_Yinc, X_Xinc, Y_Yinc, Y_Xinc;
179
180 // hacka hacka hacka ....
181 double tangent = tan(angle);
182 if(tangent >= 0.0 && tangent < 0.1) tangent = 0.1;
183 if(tangent <= 0.0 && tangent > -0.1) tangent = -0.1;
184 if(tangent > 9999) tangent = 9999;
185 if(tangent < -9999) tangent = -9999;
186
187 // sin(90) == 1; sin(270) == -1
188 if(sin(angle) > 0) // The y-values are increasing
189 {
190 Y_Ycheck = (ypos / 64) * 64 - 1;
191 Y_Xcheck = xpos + (ypos - Y_Ycheck) / tangent;
192 Y_Xinc = 64 / tangent;
193 Y_Yinc = -64;
194 } else { // The y-values are decreasing
195 Y_Ycheck = (ypos / 64) * 64 + 64;
196 Y_Xcheck = xpos + (ypos - Y_Ycheck) / tangent;
197 Y_Xinc = 64 / tangent;
198 Y_Yinc = 64;
199 }
200
201 // okay, let's check for a wall
202 while(level[(int)Y_Xcheck >> TILESHIFT][(int)Y_Ycheck >> TILESHIFT] == 0)
203 {
204 Y_Xcheck += Y_Xinc;
205 Y_Ycheck += Y_Yinc;
206 }
207
208 // cos(0) == 1; cos(180) == -1
209 if(cos(angle) > 0) // The x-values are increasing
210 {
211 X_Xcheck = (xpos / 64) * 64 + 64;
212 X_Ycheck = ypos + (xpos - X_Xcheck) * tangent;
213 X_Xinc = 64;
214 X_Yinc = 64 * tangent;
215 }
216 else // The x-values are decreasing
217 {
218 X_Xcheck = (xpos / 64) * 64 - 1;
219 X_Ycheck = ypos + (xpos - X_Xcheck) * tangent;
220 X_Xinc = -64;
221 X_Yinc = 64 * tangent;
222 }
223
224 // okay, let's check for a wall
225 while(level[(int)X_Xcheck >> TILESHIFT][(int)X_Ycheck >> TILESHIFT] == 0)
226 {
227 X_Xcheck += X_Xinc;
228 X_Ycheck += X_Yinc;
229 }
230
231 float xcheck = (X_Xcheck - xpos) * (X_Xcheck - xpos) + (X_Ycheck - ypos) * (X_Ycheck - ypos);
232 float ycheck = (Y_Xcheck - xpos) * (Y_Xcheck - xpos) + (Y_Ycheck - ypos) * (Y_Ycheck - ypos);
233
234 if(xcheck < ycheck)
235 {
236 t = (int)X_Ycheck % TILE;
237 return sqrt(xcheck);
238 }
239 else
240 {
241 t = (int)Y_Xcheck % TILE;
242 return sqrt(ycheck);
243 }
244}
Yeah, I know, it sucks I'm getting the math for CastRay() from [url http://www.permadi.com/tutorial/raycast/rayc7.html]here[/url], btw ... EDIT: Found a bug and fixed it above, but still needs work; sucker's gone back to crashing depending on location and angle >_< I'm done with it for now ... -- |
|
|