Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Error with random results

Credits go to Elverion, gnolam, LordHolNapul, and X-G for helping out!
This thread is locked; no one can reply to it. rss feed Print
Error with random results
Paladin
Member #6,645
December 2005
avatar

In my game, I have it detect when a square leaves the screen, and when this happens a variable is decreased by one which soon after this makes a new square. (In my main loop, when this variable is less than another, it makes a new square) What happens is that it only works about 4 times and the direction and even the amount of times it works is almost completely random(I have the function write to log what it does, and the log is almost always different), but it never always works. Also, sometimes the squares stop moving abruptly, and I can seem to figure it out.

1// Creates square (Just changes values of original squares)
2void create_square()
3{
4
5 squares[currentsquares].size = minsquaresize + rand() % maxsquaresize; // Random Size
6 squares[currentsquares].color = 100 + rand() % 156; // Between 100 and 25 for red
7 squares[currentsquares].speed = minspeed + rand() % maxspeed; // Random speed
8 squares[currentsquares].dir = rand() % 4; // Random Direction
9 
10 switch(squares[currentsquares].dir)
11 {
12 // Left //
13 case 0:
14 write_to_log("--Square created with direction 'LEFT'--");
15 squares[currentsquares].x = SCREEN_W;
16 squares[currentsquares].y = rand() % SCREEN_H;
17 break;
18 // Up //
19 case 1:
20 write_to_log("--Square created with direction 'UP'--");
21 squares[currentsquares].x = rand() % SCREEN_W;
22 squares[currentsquares].y = SCREEN_W;
23 break;
24 // Right //
25 case 2:
26 write_to_log("--Square created with direction 'RIGHT'--");
27 squares[currentsquares].x = 0;
28 squares[currentsquares].y = rand() % SCREEN_H;
29 break;
30 // Down //
31 case 3:
32 write_to_log("--Square created with direction 'DOWN'--");
33 squares[currentsquares].x = rand() % SCREEN_W;
34 squares[currentsquares].y = 0;
35 break;
36 }
37 currentsquares++;
38}
39 
40// Moves squares
41void update_squares()
42{
43 int i;
44 for(i = 0; i < maxsquares; i++)
45 {
46 // Destroy old square //
47 rectfill(buffer, squares<i>.x, squares<i>.y, squares<i>.x + squares<i>.size, squares<i>.y + squares<i>.size, 0);
48 switch(squares<i>.dir)
49 {
50 // Left //
51 case 0:
52 squares<i>.x = squares<i>.x - squares<i>.speed;
53 break;
54 // Up //
55 case 1:
56 squares<i>.y = squares<i>.y - squares<i>.speed;
57 break;
58 // Right //
59 case 2:
60 squares<i>.x = squares<i>.x + squares<i>.speed;
61 break;
62 // Down //
63 case 3:
64 squares<i>.y = squares<i>.y + squares<i>.speed;
65 break;
66 }
67 }
68}
69 
70// Draws the squares
71void draw_squares()
72{
73 int i;
74 for(i = 0; i < currentsquares; i++)
75 rectfill(buffer, squares<i>.x, squares<i>.y, squares<i>.x + squares<i>.size, squares<i>.y + squares<i>.size, makecol(squares<i>.color,0,0));
76
77}
78 
79// This checks when they reach the other end of the screen
80void detect_end()
81{
82 int i;
83 for(i = 0; i < currentsquares; i++)
84 {
85 switch(squares<i>.dir)
86 {
87 // Left //
88 case 0:
89 if(squares<i>.x == -squares<i>.size)
90 {
91 write_to_log("--Square left with direction 'LEFT'--");
92 currentsquares--;
93 }
94 break;
95 // Up //
96 case 1:
97 if(squares<i>.y == -squares<i>.size)
98 {
99 write_to_log("--Square left with direction 'UP'--");
100 currentsquares--;
101 }
102 break;
103 // Right //
104 case 2:
105 if(squares<i>.x == SCREEN_W + squares<i>.size)
106 {
107 write_to_log("--Square left with direction 'RIGHT'--");
108 currentsquares--;
109 }
110 break;
111 // Down //
112 case 3:
113 if(squares<i>.y == SCREEN_H + squares<i>.size)
114 {
115 write_to_log("--Square left with direction 'DOWN'--");
116 currentsquares--;
117 }
118 break;
119 }
120 }
121}
122 
123// This is the main loop
124 
125 if(currentsquares < maxsquares)
126 create_square();
127 update_squares();
128 draw_squares();
129 detect_collision(mx, my);
130 detect_end();
131 rest(10);

That is all the code involving the squares, and I can't seem to figure this out. If you guys could help, that would be great. I wish I could break it down further, but I really have no clue why it would do this.

X-G
Member #856
December 2000
avatar

A couple of things.

1) The square you remove from the list is most of the time not the one that left the screen
2) Your design is overall not very good. Rectfill in update()? Why?
3) What do you mean by "doesn't work"? Define the expected behaviour, and how what you get differs from it.

--
Since 2008-Jun-18, democracy in Sweden is dead. | 悪霊退散!悪霊退散!怨霊、物の怪、困った時は ドーマン!セーマン!ドーマン!セーマン! 直ぐに呼びましょう陰陽師レッツゴー!

Paladin
Member #6,645
December 2005
avatar

1) What do you mean? Are you saying that I have the wrong x and y values or something?

2) I have the rectfill in update so that it gets rid of the old square. The values change in that function, so if I didn't do it here, a drawn square would just remain where it's at while it keeps updating. (I know it's horrible, but I couldn't think of anything better)

3) I expect it to create 10 squares, then once one square leaves the screen, have that same square be replaced with new values. What happens is that they leave, and SOMETIMES the squares are replaced, but they often move about half-way across the screen, then abruptly stop, while SOMETIMES one of them moves right past it. It's very strange, but I'm not sure how to fix it.

X-G
Member #856
December 2000
avatar

1) Regardless of which square exits the screen, it's the last square in the list that gets overwritten.

2) Redraw the entire screen every logic frame.

3) See #1

--
Since 2008-Jun-18, democracy in Sweden is dead. | 悪霊退散!悪霊退散!怨霊、物の怪、困った時は ドーマン!セーマン!ドーマン!セーマン! 直ぐに呼びましょう陰陽師レッツゴー!

Paladin
Member #6,645
December 2005
avatar

1) I know that's the case, and I thought that might be the error.

2) Every logic frame? I'm relatively new to this as you might have been able to tell, so do you mean like every second or what? And how would you code this?

3) How would I avoid making it the last one every time?

Elverion
Member #6,239
September 2005
avatar

As for the logic frame, I think he means you should do something like this...

while( running )
{
  while( do_logic )
  {
    update();
  }

  draw();
}

rather than doing logic and drawing at the same time. Look further into Allegro timers and stuff to find out more about that. There was a pretty informative post towards this a few days ago if i remember correctly.

But, sorry, I don't know where your problem is. If you limit it down to a smaller portion of code where the problem might be, I'm sure many would be happy to review your code more closely.

--
SolarStrike Software - MicroMacro home - Automation software.

Paladin
Member #6,645
December 2005
avatar

Ok I've completely updated my source to make it "cleaner" and it managed to fix one problem, which where they just stopped in the middle of the screen, but now they don't always make a new square and it flickers like crazy(I used the update every logic second thing). I think I've also managed to locate the main problem which is here:

1////////////////////////////////////////////////////////////////////////////////
2// Detect End //
3// Detects when the squares reach the end of the screen //
4////////////////////////////////////////////////////////////////////////////////
5void detect_end()
6{
7 int i;
8 for(i = 0; i < maxsquares; i++)
9 {
10 switch(squares<i>.dir)
11 {
12 // Left //
13 case 0:
14 if(squares<i>.x == -squares<i>.size)
15 {
16 write_to_log("--Square left with direction 'LEFT'--");
17 replace_square(i);
18 }
19 break;
20 // Up //
21 case 1:
22 if(squares<i>.y == -squares<i>.size)
23 {
24 write_to_log("--Square left with direction 'UP'--");
25 replace_square(i);
26 }
27 break;
28 // Right //
29 case 2:
30 if(squares<i>.x == SCREEN_W + squares<i>.size)
31 {
32 write_to_log("--Square left with direction 'RIGHT'--");
33 replace_square(i);
34 }
35 break;
36 // Down //
37 case 3:
38 if(squares<i>.y == SCREEN_H + squares<i>.size)
39 {
40 write_to_log("--Square left with direction 'DOWN'--");
41 replace_square(i);
42 }
43 break;
44 }
45 }
46}

That detects when the square exits through the other end of the end, which is why I add the size of the square to the coordinates. Problem is I don't think it detects it correctly, so I've come to conclusion that this script is the problem. Here is my timer code if you are interested:

1//In startup
2install_int_ex(timer_i, BPS_TO_TIMER(60));
3 
4//In main loop
5while(timer > 0)
6 {
7 update_squares();
8 detect_collision(mx, my);
9 detect_end();
10 --timer;
11 }
12 update_screen();
13 
14//Actual timer script
15void timer_i(){
16 ++timer;
17}
18END_OF_FUNCTION(timer_i);

and like I said, that does work, but it flickers like crazy. Thanks for the help so far guys, but could you try to answer the problems above? Many thanks to you guys.

Elverion
Member #6,239
September 2005
avatar

I think I might have possibly found your problem, but it's hard to tell without more code. For each direction, you are doing this:

if(squares<i>.x == -squares<i>.size)

You should check if each squares x is less than or equal to the left boundary rather than if it is equal to -squares.size. If the speed is always 1, then it should work, but even then, it's a bad way of doing it.

As for screen flicker, need to see your update_screen() function before I can make any suggestions. Are you using a double-buffer or pageflipping? Are you vsyncing, or letting it be handled automatically(with pageflipping)?

--
SolarStrike Software - MicroMacro home - Automation software.

LordHolNapul
Member #3,619
June 2003
avatar

I've ot the same problem in my game 2 years ago... :-/
but I solved !;)

You must control the time. Windows is Multitasking and sometimes leave your application without refresh. That means that your update process will calculate an out of range X and Y position.

What you must do is to control the Delta Time while an update , and the following update, overflow from ... 30 milliseconds. In that case you must not consider this update and reset your time to the previous (so X and Y are not incremented), because all the system is going slow...

In my game I make appear a text with "CPU SLOW" message... when the system return to loop at normal speed, you can proceed with your normal X/Y updating.

Try and let me know.
8-)
If I'm right , review my game below .
Ciao

Paladin
Member #6,645
December 2005
avatar

Elverion - I put that in, and the squares didn't stop coming! YES!!! But it still flickers, and I have no idea what I'm using.

Roberto Mutti - To be completely honest with you, I have no idea what your talking about. You must remember I'm very new to C and Allegro, so I'm not very experienced.

Here is my screen code now:

1void update_screen()
2{
3 int i;
4 clear_screen();
5 for(i = 0; i < maxsquares; i++)
6 rectfill(buffer, squares<i>.x, squares<i>.y, squares<i>.x + squares<i>.size, squares<i>.y + squares<i>.size, makecol(squares<i>.color,0,0));
7
8}
9 
10void clear_screen(void)
11{
12 // Clear Screen //
13 rectfill(screen, 0, 0, SCREEN_W, SCREEN_H, 0);
14 // Clear Buffer //
15 rectfill(buffer, 0, 0, buffer->w, buffer->h, 0);
16}

That's the code I used, thank god you guys figured it out.

X-G
Member #856
December 2000
avatar

1) Don't clear the screen,

2) Where do you blit the buffer to screen?

--
Since 2008-Jun-18, democracy in Sweden is dead. | 悪霊退散!悪霊退散!怨霊、物の怪、困った時は ドーマン!セーマン!ドーマン!セーマン! 直ぐに呼びましょう陰陽師レッツゴー!

Paladin
Member #6,645
December 2005
avatar

1) Why not clear the screen? If I didn't, wouldn't the old squares still be there?

2) I use buffer with my main menu when I want to keep my mouse in front of the text. I used it in a really crappy method, but it works.

gnolam
Member #2,030
March 2002
avatar

Quote:

1) Why not clear the screen? If I didn't, wouldn't the old squares still be there?

No, because you'll blit your buffer over it.

Quote:

2) I use buffer with my main menu when I want to keep my mouse in front of the text. I used it in a really crappy method, but it works.

That... didn't answer X-G's question. :P

--
Move to the Democratic People's Republic of Vivendi Universal (formerly known as Sweden) - officially democracy- and privacy-free since 2008-06-18!

Elverion
Member #6,239
September 2005
avatar

Brian, don't pay too much mind to the delta-time method...it's overly complicated and is a bit difficult for newer programmers to come to grasp with. Try using the fixed logic method instead until you think you are ready for delta-time.

Now about the flickering, this is caused because of the vertical retrace. See, your monitor and your screen bitmap are not in sync with each other, which is why it's getting all flickery. To fix this, call vsync(); just before you blit your buffer to the screen.

Also, in your clear_screen function, it looks like you can remove

 // Clear Screen //
 rectfill(screen, 0, 0, SCREEN_W, SCREEN_H, 0);

Since you should be drawing over top of the old screen with the new buffer, clearing the screen would be wasteful.

--
SolarStrike Software - MicroMacro home - Automation software.

Paladin
Member #6,645
December 2005
avatar

Oh, so you want me to blit a blank buffer on there, ok. I was blitting text to a buffer, then blitting the buffer to the screen, then the cursor to the screen that way when I "hovered" over the options, the mouse would stay on top. I didn't really intend on using it to cover everything up. To use vsync, I only need to type in
vsync();
before I blit all the stuff to the screen? If so, I'll implement that as soon as possible. Also, is there any way I can read more about this "delta-time" method? I've never really heard of it, and there's other methods I'm not familiar with such as pageflipping, triple-buffer, or whatever those are. Thanks for all the help guys, this is really awesome.

EDIT: I added vsync, works real nice, now it seems to jump a little bit here and there and seems choppy, but the flickering definantely stopped. I am using a very, very flawed version of detecting collisions that I was hoping you guys could look over. What I did was I detected every single pixel on the edges of the cursor, and I was wondering if there was an easier way? Here's the code I've been using:

1void detect_collision(int x, int y)
2{
3 int i;
4 int half = CURSORSIZE / 2;
5
6 // Left Side //
7 for(i = -half; i <= half; i++)
8 {
9 if(getpixel(screen, x - half, y + i) != 0) // Not Equal to Black
10 textprintf_centre_ex(buffer, font, SCREEN_W/2, 10, makecol(rand() % 255, rand() % 255, rand() % 255), -1, "COLLISION!");
11 }
12 // Top Side //
13 for(i = -half; i <= half; i++)
14 {
15 if(getpixel(screen, x + i, y - half) != 0) // Not Equal to Black
16 textprintf_centre_ex(buffer, font, SCREEN_W/2, 10, makecol(rand() % 255, rand() % 255, rand() % 255), -1, "COLLISION!");
17 }
18 // Right Side //
19 for(i = -half; i <= half; i++)
20
21 {
22 if(getpixel(screen, x - half, y + i) != 0) // Not Equal to Black
23 textprintf_centre_ex(buffer, font, SCREEN_W/2, 10, makecol(rand() % 255, rand() % 255, rand() % 255), -1, "COLLISION!");
24 }
25 // Bottom Side //
26 for(i = -half; i <= half; i++)
27 {
28 if(getpixel(screen, x + i, y + half) != 0) // Not Equal to Black
29 textprintf_centre_ex(buffer, font, SCREEN_W/2, 10, makecol(rand() % 255, rand() % 255, rand() % 255), -1, "COLLISION!");
30 }
31 
32}

I hope this is my last question for awhile. Thanks everyone.

Elverion
Member #6,239
September 2005
avatar

I think you should try page-flipping, it removed that jerkiness for me, and I've been using it since. Actually, I setup a class to attempt to use page-flipping, and if it cannot be setup (OS does not allow for multipul video bitmaps), then it will fall back on a simple memory double-buffer. It works pretty well, and I could give you the source code to it. It's been tested on Windows and Gentoo Linux.

Simple square collisions can be done with "collision boxes". That is, you setup a rectangle, and simply check if something is within those points. Here's a chunk of code from a menu button in my game.

1bool CBasicButton::point_is_bounding(int px, int py)
2{ // check if (px,py) is within the button, return true if it is
3 if( (px >= x) && (px <= x + graphic->w) )
4 {
5 if( (py >= y) && (py <= y + graphic->h) )
6 return true;
7 else
8 return false;
9 }
10 else
11 return false;
12}
13 
14int CBasicButton::handle_clicks()
15{ // mb1 and mb2 hold the mouse button states.
16 // one is "previous", one is "now"
17 static int mb1, mb2 = 0;
18 
19 mb2 = mb1;
20 mb1 = mouse_b;
21
22 // if the user has just released the left mouse button, return it
23 // 1 represents left mouse button, 2 will represent right mouse button
24 if( (!mb1 & 1) && (mb2 & 1) && point_is_bounding(mouse_x, mouse_y) )
25 return 1;
26 else
27 return 0;
28}

--
SolarStrike Software - MicroMacro home - Automation software.

Paladin
Member #6,645
December 2005
avatar

Yeah, if you could show me how to use pageflipping, that would be great. And for the collisions, I'm surprised I didn't think of that before. Thanks a lot.

Elverion
Member #6,239
September 2005
avatar

I'll attach my screensystem source, with a well commented example on how to use it, and the Dev-CPP project file. It's very simple; you almost can't screw it up. You can use it however you want, with no need to give me credit(as if it's all that difficult and deserved credit).

Anyways, page-flipping is simply creating an array of 2 BITMAP *s, then you draw to page one, flip it to the screen, and while that one is being used as the screen bitmap, you draw to the other. You just keep switching between which one is getting drawn, and which one is getting drawn to.

Setup:

BITMAP *pages[2] = NULL;
pages[0] = create_video_bitmap(w, h);
pages[1] = create_video_bitmap(w, h);

int page = 0;

draw/flip:

show_video_bitmap(pages[page]);
page = (page+1) % 2; // this makes it 1 if it was 0, and 0 if it was 1

--
SolarStrike Software - MicroMacro home - Automation software.

Paladin
Member #6,645
December 2005
avatar

I honestly looked through it, but I don't know how C++ works, so is there a way I can use this with C?

Elverion
Member #6,239
September 2005
avatar

Nope, I guess not. Just use the chunks of code from my last post for creating your page-flipping mechanism, and drawing it. then, to draw onto your buffer, draw to pages[page]. I'm sure you can figure out how to do it. If you need more information, consult the allegro manual.

--
SolarStrike Software - MicroMacro home - Automation software.

Paladin
Member #6,645
December 2005
avatar

Ok, I guess I won't be learning anything unless I actually try it completely on my own. Thanks for all your help guys.

Go to: