More efficient way to capture mouse position?
Mishtiff

Currently I am updating mouse positions whenever the mouse is moved:

//prev ev.type code

else if(ev.type == ALLEGRO_EVENT_MOUSE_AXES)
{
mousex = ev.mouse.x;
mousey = ev.mouse.y;
}

however this completely shuts off:

if(render && al_is_event_queue_empty(event_queue))

whenever the mouse moves for a period of time. Is there a more efficient way of capturing the mouse location? The only time i need to save its location would be on right mouse click or a key press.

UPDATE: I did find that if I use mouse_event_button_down it will save the data, but using the keyboard to save the data does not seem to work.

pkrcel
Mishtiff said:

Currently I am updating mouse positions whenever the mouse is moved

If you need to value the mouse position against a specific coordinate (i.e. to check if it's on some GUI element) this is basically overkill.

Quote:

however this completely shuts off:

 if(render && al_is_event_queue_empty(event_queue))

Well, that completely puts me a bit off...I guess you get a lot of stutter, but not a complete rendering stop.

Quote:

I did find that if I use mouse_event_button_down it will save the data, but using the keyboard to save the data does not seem to work.

What does "using the keyboard to save the data" mean exactly?

Mishtiff
pkrcel said:

If you need to value the mouse position against a specific coordinate (i.e. to check if it's on some GUI element) this is basically overkill.

Agreed, that is why i present the question. :) I suppose my problem is that I am unaware how i can save the mouse position when an event is calling for it outside of a ev.type == mouse_event

pkrcel said:

Well, that completely puts me a bit off...I guess you get a lot of stutter, but not a complete rendering stop.

Yeah at the moment my coding needs some tweaking for efficiency. So if i constantly move the mouse in the display, the display never updates (since it is constantly updating the mouse_pos every single pixel.

pkrcel said:

What does "using the keyboard to save the data" mean exactly?

example:

#SelectExpand
1if(ev.type == ALLEGRO_EVENT_KEY_DOWN) 2{ 3 switch(ev.keyboard.keycode) 4 { 5 case ALLEGRO_KEY_SPACE: 6 [SPACE] = true; 7 mousex = ev.mouse.x; 8 mousey = ev.mouse.y; 9 break; 10 } 11}

pkrcel
Mishtiff said:

example:

AH now I got it....uhm I guess it wouldn't work by design, since the trapped event is NOT related to the mouse, so (on windows, I suppose...but logically on every system oculd be like that) the trapped OS event message should not contain this info.

This is my speculation of course, but I'm quite confident that you aren't expected to use the event mouse fields when trapping a keystroke.... I may stand corrected of course.

On the other hand why would you do so? just curious...I might imagine but still...

Anyway at any time you should be able to interrogate the mouse through the input API....lesseee.... I guess al_get_mouse_state should do for you.

I suppose you example could read as

if(ev.type == ALLEGRO_EVENT_KEY_DOWN){ 
   switch(ev.keyboard.keycode){
      case ALLEGRO_KEY_SPACE: 
      [SPACE] = true;            // I kind of like this syntax ^___^
      ALLEGRO_MOUSE_STATE state;
      al_get_mouse_state(&state)
      mousex = state.x; 
      mousey = state.y; 
      break; 
      }
   }

Elias

Can you show more code? A typical mouse shouldn't produce more events than say 1000 per second - I can't see how that would affect your rendering in any way even on the slowest and most ancient CPU.

Mishtiff
pkrcel said:

1. On the other hand why would you do so?
2. I suppose you example could read as:

1A. currently in the game i'm making, you move your ships to mouse_pos with the space bar(which i have now changed to the right click on mouse, making it much more simple...)
However, saving the mouse position every pixel movement was causing too many event calls and would shut down my rendering because of all the mouse updates. NOTE: I have a system set up in my game for collisions and moment which is VERY inefficient, and is more than likely causing the mouse update to be worse that it should be.
I am still trying to figure out how in the heck KD-Trees work. they are pretty confusing to me at the moment. So what i have done in my game is:
split the screen into 12 quadrants, and save each ship into its quadrant list. when the collision decisions come, all ships are tested with ships in the same quadrant instead of every ship in the game.

2A. this seems to be exactly what i am looking for. However, by the way you are talking it sounds like this is a bad way to go about it?

Elias said:

Can you show more code?

of course. Ill post my code here soon. its pretty messy so let me clean it up and get some better comments into it.

Thomas Fjellstrom
Mishtiff said:

2A. this seems to be exactly what i am looking for. However, by the way you are talking it sounds like this is a bad way to go about it?

Store the mouse pos in the mouse events, then only move the ships when space is pressed. could have a next_shipx, next_shipy pair, and then only set the ship's coords after space is pressed?

Quote:

However, saving the mouse position every pixel movement was causing too many event calls and would shut down my rendering because of all the mouse updates.

Mouse events alone won't case that to happen. Just store the mouse coords on mouse update. then once a "tick" (ie: timer tick) run your collision detection, not when ever the mouse is moved.

pkrcel

Mouse events alone won't case that to happen. Just store the mouse coords on mouse update. then once a "tick" (ie: timer tick) run your collision detection, not when ever the mouse is moved.

This. Absolutely this.

I didn't understand fully what was the problem here, and I didn't think it could be related to your other thread about Collision detection.

But if you are running collision detection at each mouse axis event, THEN you're surely wasting a TON of CPU cycles (*even* if the collision detection is perfectly optimized).

Why wasted? Because no object has yet moved when you process the events. :P

On the other hand:

Mishtiff said:

2A. this seems to be exactly what i am looking for. However, by the way you are talking it sounds like this is a bad way to go about it?

Well, polling the state API while handling events is sort of double-defeating the purpose of both....BUT in this simple case it should work flawlessly so it might be good enuff.

Nevertheless, Tomasu suggestion is the way to go in my opinion, store the mouse coords and use'em as a "next frame prediction".

Quote:

I am still trying to figure out how in the heck KD-Trees work. they are pretty confusing to me at the moment. So what i have done in my game is:
split the screen into 12 quadrants, and save each ship into its quadrant list. when the collision decisions come, all ships are tested with ships in the same quadrant instead of every ship in the game.

At the very heart of the problem, what you have done is the SEED of space partitioning.
Trees are an efficient data structure to store this partition, because there are very efficient algorithms in which you can traverse them.

I woudl say good job, nevertheless.

Mishtiff

Store the mouse pos in the mouse events, then only move the ships when space is pressed. could have a next_shipx, next_shipy pair, and then only set the ship's coords after space is pressed?

this was my coding for the mouse update, so i dont see a reason for the slowdown to occur, yet it did:

#SelectExpand
1else if(ev.type == ALLEGRO_EVENT_MOUSE_AXES) 2{ 3 mousex = ev.mouse.x; 4 mousey = ev.mouse.y; 5}

Mouse events alone won't case that to happen. Just store the mouse coords on mouse update. then once a "tick" (ie: timer tick) run your collision detection, not when ever the mouse is moved.

I already run my collision detection only in the timer tick. I will just attach my code here and see if you guys can find what exactly is causing such dramatic slow downs.

pkrcel said:

This. Absolutely this.

I didn't understand fully what was the problem here, and I didn't think it could be related to your other thread about Collision detection.

But if you are running collision detection at each mouse axis event, THEN you're surely wasting a TON of CPU cycles (*even* if the collision detection is perfectly optimized).

Why wasted? Because no object has yet moved when you process the events. :P

yeah, but as i have stated above, it was not in the same block of code.

pkrcel said:

Well, polling the state API while handling events is sort of double-defeating the purpose of both....BUT in this simple case it should work flawlessly so it might be good enuff.

Nevertheless, Tomasu suggestion is the way to go in my opinion, store the mouse coords and use'em as a "next frame prediction".

ok i understand, and it is beginning to sound like it is due to my coding that is more of the problem, rather than the updating and saving of the mouse coords.

pkrcel said:

At the very heart of the problem, what you have done is the SEED of space partitioning.
Trees are an efficient data structure to store this partition, because there are very efficient algorithms in which you can traverse them.

I woudl say good job, nevertheless.

Thank you, i was pretty happy with how my old collision detection worked. i have, however, found an amazing post with a

pkrcel

....a cliffhanger? :P

Trent Gamblin

I wouldn't use that code. Use:

while (!al_event_queue_is_empty()) {
    // process events
}

if (render) {
    // render
}

Otherwise you could skip a frame sometimes, even if it's just 1 it's not right.

Mishtiff
pkrcel said:

....a cliffhanger? :P

I guess it cut out the rest of what i wrote... What i had said was:

Thank you, i was pretty happy with how my old collision detection worked. i have, however, found an amazing post with a adaptive quadtree. I think i have implemented it well, im just unsure if i am using it efficiently. Ill let you be the judge.

while (!al_event_queue_is_empty()) {
// process events
}

if (render) {
// render
}

Where am i supposed to put this? in the timer tick? that doesnt make sense to me, sorry.

Edgar Reynaldo
Mishtiff said:

while (!al_event_queue_is_empty()) {
// process events
}

if (render) {
// render
}

Where am i supposed to put this? in the timer tick? that doesnt make sense to me, sorry.

No put it around your event processing loop.

do {
   if (ev.type == ALLEGRO_EVENT_*_*) {/*...do stuff...*/}
} while (!al_is_event_queue_empty(queue));

if (render) {draw();render = false;}

Mishtiff

Okay i have put it around all of my event queues, but it is not drawing any frames now. probably because by the time it makes it through all of the events in timer.tick, it already has a timer.tick in the queue? heres what it looks like:

#SelectExpand
1while(!done) 2 { 3 ALLEGRO_EVENT ev; 4 al_wait_for_event(event_queue, &ev); 5 6 //============================================== 7 //INPUT 8 //============================================== 9 do { 10 if(ev.type == ALLEGRO_EVENT_KEY_DOWN) 11 { 12 switch(ev.keyboard.keycode) 13 { 14 case ALLEGRO_KEY_ESCAPE: 15 done = true; 16 break; 17 case ALLEGRO_KEY_A: 18 keys[A] = true; 19 break; 20 case ALLEGRO_KEY_D: 21 keys[D] = true; 22 break; 23 case ALLEGRO_KEY_W: 24 keys[W] = true; 25 break; 26 case ALLEGRO_KEY_S: 27 keys[S] = true; 28 break; 29 case ALLEGRO_KEY_SPACE: 30 if(state == TITLE) 31 ChangeState(state, PLAYING); 32 else if(state == PLAYING) 33 { 34 keys[SPACE] = true; 35 } 36 else if(state == LOST) 37 ChangeState(state, PLAYING); 38 break; 39 } 40 } 41 else if(ev.type == ALLEGRO_EVENT_KEY_UP) 42 { 43 switch(ev.keyboard.keycode) 44 { 45 case ALLEGRO_KEY_ESCAPE: 46 done = true; 47 break; 48 case ALLEGRO_KEY_A: 49 keys[A] = false; 50 break; 51 case ALLEGRO_KEY_D: 52 keys[D] = false; 53 break; 54 case ALLEGRO_KEY_W: 55 keys[W] = false; 56 break; 57 case ALLEGRO_KEY_S: 58 keys[S] = false; 59 break; 60 case ALLEGRO_KEY_SPACE: 61 keys[SPACE] = false; 62 break; 63 } 64 }/* 65 else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) 66 { 67 keys[SPACE] = true; 68 mousex = ev.mouse.x; 69 mousey = ev.mouse.y; 70 } 71 else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) 72 { 73 keys[SPACE] = false; 74 }*/ 75 else if(ev.type == ALLEGRO_EVENT_MOUSE_AXES) 76 { 77 mousex = ev.mouse.x; 78 mousey = ev.mouse.y; 79 } 80 //============================================== 81 //GAME UPDATE 82 //============================================== 83 else if(ev.type == ALLEGRO_EVENT_TIMER) 84 { 85 render = true; 86 quad->Clear(); 87 88 //UPDATE FPS=========== 89 90 //===================== 91 92 if(state == PLAYING) 93 { 94 if(al_current_time() - gameTime >= 1) 95 { 96 gameTime = al_current_time(); 97 gameFPS = frames; 98 frames = 0; 99 } 100 101 102 //Spawn More Ships 103 spawner++; 104 if (spawner >= 60) 105 { 106 for(Baseiter = Bases.begin(); Baseiter != Bases.end(); ++Baseiter) 107 { 108 if((*Baseiter)->GetShipCount() > 9) continue; //increase this number to spawn more ships 109 Ship *ship = new Ship(); 110 ship->Init(); 111 ship->SetPlayerID((*Baseiter)->GetPlayerID()); 112 ship->SetX(rand() % 40 + (*Baseiter)->GetX() - 20); 113 ship->SetY(rand() % 40 + (*Baseiter)->GetY() - 20); 114 ship->SetColor((*Baseiter)->GetColor()); 115 116 (*Baseiter)->SetShipCount(1); //increase base ship count 117 objects.push_back(ship); //add ship to objects and to ships 118 ships.push_back(ship); 119 } 120 spawner = 0; 121 } 122 123 //used to upgrade ships (VERY BASIC----later will be used after leveling) 124 if(keys[SPACE]) //used to move ships to location of mouse 125 { 126 127 for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter) 128 { 129 if((*Shipiter)->GetPlayerID() != 1) continue; 130 (*Shipiter)->SetDestX(mousex); 131 (*Shipiter)->SetDestY(mousey); 132 (*Shipiter)->setMoving(true); 133 } 134 } 135 if(keys[W]) //increase ship speed 136 { 137 138 for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter) 139 { 140 if((*Shipiter)->GetPlayerID() != 1) continue; 141 (*Shipiter)->setSpeed(.05); 142 } 143 } 144 145 if(keys[S]) //increase ship damage 146 { 147 148 for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter) 149 { 150 if((*Shipiter)->GetPlayerID() != 1) continue; 151 (*Shipiter)->setAttack(5); 152 } 153 std::cout << std::endl; 154 } 155 156 if(keys[A]) //increase ship range 157 { 158 159 for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter) 160 { 161 if((*Shipiter)->GetPlayerID() != 1) continue; 162 (*Shipiter)->setRange(5); 163 } 164 std::cout << std::endl; 165 } 166 167 if(keys[D]) //increase ship health 168 { 169 170 for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter) 171 { 172 if((*Shipiter)->GetPlayerID() != 1) continue; 173 (*Shipiter)->setHealth(5); 174 } 175 std::cout << std::endl; 176 } 177 178 179 //Update objects positions/animations and add to quad tree. 180 for(iter = objects.begin(); iter != objects.end(); ++iter) 181 { 182 (*iter)->Update(); 183 if((*iter)->GetID() == BASE) continue; 184 quad->AddObject(*iter); 185 } 186 187 //used for collision detection (if this is even somewhat correct) //At the moment, i just randomly move units until there is no collision 188 for(iter = objects.begin(); iter != objects.end(); ++iter) 189 { 190 if((*iter)->GetID() == BASE) continue; 191 vector<GameObject*> returnedObjects; 192 returnedObjects = quad->GetObjectsAt((*iter)->GetX(), (*iter)->GetY()); 193 if(returnedObjects.size() < 2) continue; 194 for(int i = 0; i != returnedObjects.size(); i++) 195 { 196 if((*iter) == returnedObjects[i]) continue; 197 if((*iter)->CheckCollisions(returnedObjects[i])) 198 { 199 returnedObjects[i]->SetX(returnedObjects[i]->GetX() + (-5 + rand() % 10)); 200 returnedObjects[i]->SetY(returnedObjects[i]->GetY() + (-5 + rand() % 10)); 201 } 202 } 203 } 204 205 206 207 208 //cull the dead 209 for(Shipiter = ships.begin(); Shipiter != ships.end(); ) 210 { 211 if(!(*Shipiter)->GetAlive()) 212 { 213 Shipiter = ships.erase(Shipiter); //remove from ships 214 } 215 else 216 Shipiter++; 217 } 218 for(iter = objects.begin(); iter != objects.end(); ) //remove from objects and give credit to base that killed 219 { 220 if(! (*iter)->GetAlive()) 221 { 222 if((*iter)->GetID() == SHIP) 223 { 224 int id = (*iter)->GetPlayerID(); 225 for(Baseiter = Bases.begin(); Baseiter != Bases.end(); Baseiter++) 226 { 227 if((*Baseiter)->GetPlayerID() == id) 228 { 229 (*Baseiter)->SetShipCount(-1); 230 int killer = (*iter)->GetShipKiller(); 231 for(Baseiter2 = Bases.begin(); Baseiter2 != Bases.end(); ) 232 { 233 if((*Baseiter2)->GetPlayerID() == killer) 234 {(*Baseiter2)->setScore(1); break;} 235 else 236 Baseiter2++; 237 } 238 break; 239 } 240 } 241 242 } 243 (*iter)->Destroy(); 244 delete (*iter); 245 iter = objects.erase(iter); 246 247 } 248 else 249 iter++; 250 } 251 } 252 } 253 } while (!al_is_event_queue_empty(event_queue)); 254 255 256 //============================================== 257 //RENDER 258 //============================================== 259 260 if(render) 261 { 262 render = false; 263 frames++; 264 265 266 //BEGIN PROJECT RENDER================ 267 if(state ==TITLE) 268 {} 269 else if(state == PLAYING) 270 { 271 al_draw_textf(font18, al_map_rgb(255, 0, 255), 5, 35, 0, "FPS: %i mouse: %f,%f", gameFPS, mousex, mousey); //display FPS on screen 272 273 274 275 for(iter = objects.begin(); iter != objects.end(); ++iter) 276 (*iter)->Render(); 277 278 //quad->draw(); //used to draw visual for quadrants 279 } 280 else if(state == LOST) 281 {} 282 283 //FLIP BUFFERS======================== 284 al_flip_display(); 285 al_clear_to_color(al_map_rgb(0,0,0)); 286 } 287 }

EDIT::

The al_wait_for_event should be in the do-while loop too, otherwise if there are any events in the queue that loop will never end because they never get removed.

This corrected the problem. Going to see how it runs now if i add more units.

Trent Gamblin

The al_wait_for_event should be in the do-while loop too, otherwise if there are any events in the queue that loop will never end because they never get removed.

Mishtiff

After changing the code to what we have discussed, it freezes at loading ~9-10 units. I checked to see the amount of time it takes for the code to run through the timer.tick part, and it takes around ~.005. im running the game at 60 fps, which means the timer should be every .0166. not sure what is going on to make it stop rendering. ill try to add some std::cout to see why it is not reaching the render state.

EDIT:: after checking, it seems that the game gets stuck with the al_is_event_queue_empty(). After 9 to 10 units have been spawned per base (90-100 ships) the game stays in the do while loop

Trent Gamblin

Ok, what you want to do is use al_get_next_event instead of al_wait_for_event. Something like I had posted before:

while (!al_event_queue_is_empty(queue)) {
    ALLEGRO_EVENT event;
    al_get_next_event(queue, &event);
    // process your events
}

Change your do-while loop to this while loop.

Mishtiff

Ok, what you want to do is use al_get_next_event instead of al_wait_for_event. Something like I had posted before:

This makes a lot of sense, thank you. Ill mess around some more now that it is running correctly and update on anything further :)

Thomas Fjellstrom

If you call that function instead, you'll have to make sure to check that there is actually a new event because al_get_next_event always returns right away, even if there is no new event. Ie:

if(!al_get_next_event(&event)) continue;

Otherwise you'll be attempting to handle invalid data (the first time) and old events (any time after the first event).

Or even make that loop:

ALLEGRO_EVENT event;
while(al_get_next_event(&event)) { ... }

Personally, I prefer the layout of the loop I used in the tutorials on the wiki. Keeps from having nested loops. But its just a personal preference.

Mishtiff

Personally, I prefer the layout of the loop I used in the tutorials on the wiki. Keeps from having nested loops. But its just a personal preference.

May i have a link to this? Sorry i'm unsure where to look for this (other than on wiki, not sure what to search for exactly). Ill probably go with a layout that does not require a nested loop.

The way it is currently set up doesnt seem to work anyways, since my game will not update unless i move the mouse... interesting. Hard to understand why that is occurring. the timer loop is running and everything, but until i move the mouse, it wont render, and as long as i move the mouse it will, unless i stop moving.

Thomas Fjellstrom

Try this code.

Mishtiff

Thank you for the link, i could not seem to locate it (i did find the camera tut that added a new depth for me to try ;D)

Is the program I am trying to create just too much for A5 to handle, or am I just writing awful code? Having frame rate issues still, which is disappointing considering how little i am actually trying to do.

Thomas Fjellstrom

It's probably your code, or your hardware.

pkrcel

Allegro5 is production-grade software (go look at factorio game for a very good indication), so basically we have to find the culprit in your code (which not necessarily has to be that awful :P ).

I am also under the impression that we are circling around the problem in framerate but are looking at the input handling, which honestly SHOULDN'T be the issue-dealer.

Thomas Fjellstrom

The thing that tends to cause framerate issues the most with A5 projects is loading bitmaps before you create the display. That creates memory bitmaps which are much slower to draw than display bitmaps.

Trent Gamblin

Thomas, it already did check if there were events... you're just confusing. al_event_queue_is_empty already checks in the code I gave him. That is the best form of the loop, despite what your preference is, in actual games.

Mishtiff
pkrcel said:

1.Allegro5 is production-grade software

2.I am also under the impression that we are circling around the problem in framerate but are looking at the input handling, which honestly SHOULDN'T be the issue-dealer.

1A. Thank you, that is reassuring. It is obviously my coding that is the culprit (not that i wasn't already expecting that).
2A.Well tell me where to start looking and ill start working on fixing up some code. In all honestly, as of now in the game, all I am asking the game to do is:
1.check user input.
2. on timer tick: update render to true, spawn ships if shipspawner == 60, update all objects and add to quadrant system, check collisions, and destroy dead ships
3. Render if true, outside of timer.tick
seems basic enough to me. I am also not using any bitmaps what-so-ever. I'm only drawing primitives. All they consist of is al_draw_circles for every ship and even the bases.

1.or your hardware.
2. The thing that tends to cause framerate issues the most with A5 projects is loading bitmaps before you create the display. That creates memory bitmaps which are much slower to draw than display bitmaps.

1A. My hardware is strong enough to run most games on high-ultra. Of course most are obviously coded more efficiently than my own ;) :P But again, i dont see how i can barely run what i am wanting my code to do, since i feel like its so little.
2A. Understood for future notice. If you read above you'll see I use nothing for bitmaps currently.

Thomas, it already did check if there were events... you're just confusing. al_event_queue_is_empty already checks in the code I gave him. That is the best form of the loop, despite what your preference is, in actual games.

If this is true i would have no problem using it, but:
It is weird, after I implemented the code to the nested style you had, my game would not render unless I moved my mouse. It is still doing it after i have reverted to the while(!done) only. Not sure how to fix it, still looking into it.

Trent Gamblin

It would only render if you're moving the mouse if you're using al_wait_for_event but it WAITS in that call until it gets an event.

Mishtiff

It would only render if you're moving the mouse if you're using al_wait_for_event but it WAITS in that call until it gets an event.

Thank you, even though it clearly says that in the function call, for whatever reaso i wasnt aware that it sits there until something happens. I figured it skipped over it until it had something in the queue.
Also, i see why your while loop makes more sense for a game loop

Trent Gamblin

al_wait_for_event - if there is an event ready, grabs it, otherwise waits

al_get_next_event - if there is an event ready, grabs it and returns true, otherwise returns false without ever waiting

What I was saying about not having to check if there is an event returned from al_get_next_event, is because you already know there is an event in the queue because you called al_is_event_queue_empty.

Thomas Fjellstrom
Mishtiff said:

Also, i see why your while loop makes more sense for a game loop

I honestly don't. They should be functionally identical. My loop just acts as both loops at once.

Feel free to explain it to me.

Trent Gamblin

Your loop does needless processing. Not much but it's not perfect like mine. :P

Thomas Fjellstrom

Your loop does needless processing. Not much but it's not perfect like mine. :P

Ok, maybe... But I still don't see how it does more than yours. Both check the events, and the queue for more events. Maybe it executes the if(draw) more often, but its almost always 0, so the cpu will predict that out of the pipeline before it becomes a problem.

Mishtiff

I am trying to understand both as well now. let me explain what i understand for both.

Trents:

#SelectExpand
1while(!done) 2{ 3 4 while(!al_is_event_queue_empty(event_queue)) 5 { 6 ALLEGRO_EVENT ev; 7 al_get_next_event(event_queue, ev); 8 //do all events 9 } 10 11if(render) {} 12}

For this version, all i see it doing is running through the while(!done) loop constantly until the event queue has something in it. obviously the render portion will be false until the timer.tick has ran, so it does seem running code this way is unnecessary?

Thomas's:

#SelectExpand
1while(!done) 2{ 3 ALLEGRO_EVENT ev; 4 al_wait_for_event(event_queue, ev); 5 //do event 6 7if(render && al_is_event_queue_empty(event_queue)){} 8}

For Thomas's, it simply waits to run any code until it is given input, then runs the code. if the code was a timer.tick, it will automatically pass over the if(render) statement.

However... In Trents version, i understand that it will do any events in the queue until the queue is empty, then it will run into the render. after that it will circle around and hit the event loop, circle through all saved events, then continue again to the render.

The thing about Trents is that as long as queue events are being added to the queue, will it ever return to the render portion? i do understand that the computer can do most of the processes in the ev.types extremely fast so it may not matter.

you two can tell me if i have the right idea or not on each...

The thing that i still am having an issue with is:

For whatever reason, after i hit about 4-5 units spawned, it will only render if I move the mouse. the game is still constantly running the timer.tick portion (checked with std::couts), but does not render until i have moved the mouse. strange? the render portion of my code is outside of the while(!al_is_event_queue_empty) from trents code.

Thomas Fjellstrom

Hm, that first loop will sit and spin at 100% even when there's nothing to do.

Otherwise they are functionally equivalent. Both will handle events as they come in.

In the bottom loop, it checks for an empty queue to make sure all events are handled, including piled up timer events, before drawing. It's so we always drain the queue, and we get free "Frame dropping" out of the deal, in case the logic and drawing take longer than your timer period, it'll skip frames to catch up. The top loop is similar in that respect, it drains the queue before drawing anything, but it uses an explicit inner loop, instead of making the one loop dual purpose (it drains the events, and handles logic+drawing, where as the inner loop does the event draining and logic, and the outer loop does the drawing).

Trent Gamblin

I actually looked at my code and I guess I do use the second loop most of the time.

What I actually have though is more like:

while (true) {
top:
  ALLEGRO_EVENT event;
  al_wait_for_event(queue, &event);
  // process event
  if (!al_event_queue_is_empty(queue)) {
    goto top;
  }
  if (render) {
    // draw
  }
}

You can replace the goto with a do-while loop very easily:

while (true) {
  do {
    ALLEGRO_EVENT event;
    al_wait_for_event(queue, &event);
    // process event
  } while (!al_event_queue_is_empty(queue));
  if (render) {
    // draw
  }
}

I think I like that last one the best out of all of them. Just remember to add a timer event source for 1/60 of a second so that the loop keeps pumping even without input.

Edgar Reynaldo

Aside from the two commented lines, that's what I posted earlier. :/

No put it around your event processing loop.

do {
   ALLEGRO_EVENT ev;// implicit
   al_wait_for_event(queue , &ev);// implicit
   if (ev.type == ALLEGRO_EVENT_*_*) {/*...do stuff...*/}
} while (!al_is_event_queue_empty(queue));

if (render) {draw();render = false;}

Trent Gamblin

You were right.

Mishtiff

Thanks for the update. Still having the problem of it not rendering until mouse movement... not sure what to do about it. Occurs after about 10 seconds, only renders if i am moving the mouse constantly, else it never renders, but the game loop is still running. IE: the units are spawning, moving, etc. but the render function never goes until i move the mouse.

Trent Gamblin

The best way to do that is...

#SelectExpand
1bool render = false; 2ALLEGRO_TIMER *timer = al_create_timer(1.0/60.0); 3al_register_event_source(queue, al_get_timer_event_source(timer)); 4al_start_timer(timer); 5 6// Inside do-while loop... 7 8if (event.type == ALLEGRO_EVENT_TIMER && event.timer.source == timer) { 9 render = true; 10} 11 12// After do-while loop... 13 14if (render) { 15 render = false; 16 // do your drawing 17}

I see in your last code post you're doing rendering where you set render = true. You should put it all after where you have if (render). If that doesn't work, show your loop as it is now.

Mishtiff

I see in your last code post you're doing rendering where you set render = true. You should put it all after where you have if (render). If that doesn't work, show your loop as it is now.

Currently my render is outside of the loop after it. I did try to put it before the loop, and still had problems. Here is what my code looks like:

#SelectExpand
1while(!done) 2 { 3 do{ 4 ALLEGRO_EVENT ev; 5 al_wait_for_event(event_queue, &ev); 6 //============================================== 7 //GAME UPDATE 8 //============================================== 9 if(ev.type == ALLEGRO_EVENT_TIMER && ev.timer.source == timer) 10 { 11 render = true; 12 13 //===================== 14 //UPDATE FPS 15 //===================== 16 17 if(state == PLAYING) 18 { 19 if(al_current_time() - gameTime >= 1) 20 { 21 gameTime = al_current_time(); 22 gameFPS = frames; 23 frames = 0; 24 } 25 26 27 //Spawn More Ships 28 spawner++; 29 if (spawner >= 60) 30 { 31 for(Baseiter = Bases.begin(); Baseiter != Bases.end(); ++Baseiter) 32 { 33 if((*Baseiter)->GetShipCount() > 19) continue; //increase this number to spawn more ships 34 Ship *ship = new Ship(); 35 ship->Init(); 36 ship->SetPlayerID((*Baseiter)->GetPlayerID()); 37 ship->SetX(rand() % 40 + (*Baseiter)->GetX() - 20); 38 ship->SetY(rand() % 40 + (*Baseiter)->GetY() - 20); 39 ship->SetColor((*Baseiter)->GetColor()); 40 41 (*Baseiter)->SetShipCount(1); //increase base ship count 42 objects.push_back(ship); //add ship to objects and to ships 43 ships.push_back(ship); 44 } 45 spawner = 0; 46 } 47 48 //used to upgrade ships (VERY BASIC----later will be used after leveling) 49 if(keys[SPACE]) //used to move ships to location of mouse 50 { 51 52 for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter) 53 { 54 if((*Shipiter)->GetPlayerID() != 1) continue; 55 (*Shipiter)->SetDestX(mousex); 56 (*Shipiter)->SetDestY(mousey); 57 (*Shipiter)->setMoving(true); 58 } 59 } 60 if(keys[W]) //increase ship speed 61 { 62 63 for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter) 64 { 65 if((*Shipiter)->GetPlayerID() != 1) continue; 66 (*Shipiter)->setSpeed(.05); 67 } 68 } 69 70 if(keys[S]) //increase ship damage 71 { 72 73 for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter) 74 { 75 if((*Shipiter)->GetPlayerID() != 1) continue; 76 (*Shipiter)->setAttack(5); 77 } 78 std::cout << std::endl; 79 } 80 81 if(keys[A]) //increase ship range 82 { 83 84 for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter) 85 { 86 if((*Shipiter)->GetPlayerID() != 1) continue; 87 (*Shipiter)->setRange(5); 88 } 89 std::cout << std::endl; 90 } 91 92 if(keys[D]) //increase ship health 93 { 94 95 for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter) 96 { 97 if((*Shipiter)->GetPlayerID() != 1) continue; 98 (*Shipiter)->setHealth(5); 99 } 100 std::cout << std::endl; 101 } 102 103 104 //Update objects positions/animations and add to quad tree. 105 quad->Clear(); 106 for(iter = objects.begin(); iter != objects.end(); ++iter) 107 { 108 (*iter)->Update(); 109 if(!(*iter)->GetID() == BASE) 110 quad->AddObject(*iter); 111 } 112 113 //used for collision detection (if this is even somewhat correct) //At the moment, i just randomly move units until there is no collision 114 if(al_is_event_queue_empty(event_queue)) 115 { 116 for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter) 117 { 118 vector<GameObject*> returnedObjects; 119 returnedObjects = quad->GetObjectsAt((*Shipiter)->GetX(), (*Shipiter)->GetY()); 120 if(returnedObjects.size() < 2) continue; 121 for(int i = 0; i != returnedObjects.size(); i++) 122 { 123 if((*Shipiter) == returnedObjects[i]) continue; 124 if((*Shipiter)->CheckCollisions(returnedObjects[i])) 125 { 126 returnedObjects[i]->SetX(returnedObjects[i]->GetX() + (-5 + rand() % 10)); 127 returnedObjects[i]->SetY(returnedObjects[i]->GetY() + (-5 + rand() % 10)); 128 } 129 } 130 } 131 } 132 133 134 //cull the dead 135 for(Shipiter = ships.begin(); Shipiter != ships.end(); ) 136 { 137 if(!(*Shipiter)->GetAlive()) 138 { 139 Shipiter = ships.erase(Shipiter); //remove from ships 140 } 141 else 142 Shipiter++; 143 } 144 for(iter = objects.begin(); iter != objects.end(); ) //remove from objects and give credit to base that killed 145 { 146 if(! (*iter)->GetAlive()) 147 { 148 if((*iter)->GetID() == SHIP) 149 { 150 int id = (*iter)->GetPlayerID(); 151 for(Baseiter = Bases.begin(); Baseiter != Bases.end(); Baseiter++) 152 { 153 if((*Baseiter)->GetPlayerID() == id) 154 { 155 (*Baseiter)->SetShipCount(-1); 156 int killer = (*iter)->GetShipKiller(); 157 for(Baseiter2 = Bases.begin(); Baseiter2 != Bases.end(); ) 158 { 159 if((*Baseiter2)->GetPlayerID() == killer) 160 {(*Baseiter2)->setScore(1); break;} 161 else 162 Baseiter2++; 163 } 164 break; 165 } 166 } 167 168 } 169 (*iter)->Destroy(); 170 delete (*iter); 171 iter = objects.erase(iter); 172 173 } 174 else 175 iter++; 176 } 177 } 178 } 179 //============================================== 180 //INPUT 181 //============================================== 182 else if(ev.type == ALLEGRO_EVENT_KEY_DOWN) 183 { 184 switch(ev.keyboard.keycode) 185 { 186 case ALLEGRO_KEY_ESCAPE: 187 done = true; 188 break; 189 case ALLEGRO_KEY_A: 190 keys[A] = true; 191 break; 192 case ALLEGRO_KEY_D: 193 keys[D] = true; 194 break; 195 case ALLEGRO_KEY_W: 196 keys[W] = true; 197 break; 198 case ALLEGRO_KEY_S: 199 keys[S] = true; 200 break; 201 case ALLEGRO_KEY_SPACE: 202 if(state == TITLE) 203 ChangeState(state, PLAYING); 204 else if(state == PLAYING) 205 { 206 keys[SPACE] = true; 207 } 208 else if(state == LOST) 209 ChangeState(state, PLAYING); 210 break; 211 } 212 } 213 else if(ev.type == ALLEGRO_EVENT_KEY_UP) 214 { 215 switch(ev.keyboard.keycode) 216 { 217 case ALLEGRO_KEY_ESCAPE: 218 done = true; 219 break; 220 case ALLEGRO_KEY_A: 221 keys[A] = false; 222 break; 223 case ALLEGRO_KEY_D: 224 keys[D] = false; 225 break; 226 case ALLEGRO_KEY_W: 227 keys[W] = false; 228 break; 229 case ALLEGRO_KEY_S: 230 keys[S] = false; 231 break; 232 case ALLEGRO_KEY_SPACE: 233 keys[SPACE] = false; 234 break; 235 } 236 } 237 else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) 238 { 239 keys[SPACE] = true; 240 mousex = ev.mouse.x; 241 mousey = ev.mouse.y; 242 } 243 else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) 244 { 245 keys[SPACE] = false; 246 } 247 } while(!al_is_event_queue_empty(event_queue)); 248 249 //============================================== 250 //RENDER 251 //============================================== 252 253 if(render) 254 { 255 render = false; 256 frames++; 257 258 259 //BEGIN PROJECT RENDER================ 260 if(state ==TITLE) 261 {} 262 else if(state == PLAYING) 263 { 264 al_draw_textf(font18, al_map_rgb(255, 0, 255), 5, 35, 0, "FPS: %i mouse: %f,%f", gameFPS, mousex, mousey); //display FPS on screen 265 266 267 for(iter = objects.begin(); iter != objects.end(); ++iter) 268 (*iter)->Render(); 269 270 //quad->draw(); //used to draw visual for quadrants 271 } 272 else if(state == LOST) 273 {} 274 275 //FLIP BUFFER======================== 276 al_flip_display(); 277 al_clear_to_color(al_map_rgb(0,0,0)); 278 } 279 }

this is my entire while(game running) code. Still having problems with it not doing the render portion after so many units have been spawned. This occurs even if i put the if(render) before the event loop. Any ideas would be excellent. If there is literally anything you see that is inefficient, yell at me. I want criticism to become a more efficient programmer.

Aikei_c

Honestly, I still don't understand how the loop with al_wait_for_event() works. I mean, shouldn't it just halt and do nothing on the al_wait_for_event() line, unless it gets some input?
I know I'm probably wrong, but I still don't know why. I tried to read this thread several times, but still can't quite understand it.

EDIT: Oh well, nevermind, I see he catches a timer event.

EDIT2: Mishtiff, do you create register your timer with the event queue properly? You probably do, or your game code would not ever be executed at all.

EDIT3: Ok, look at what you are doing. You are running all your game code, including collision detection etc. WHILE EVENT QUEUE IS NOT EMPTY. But since your game code can take quite some time, you can receive a new event while running through this loop, so you will have to repeat it again... and again... never reaching your render code.

Mishtiff

al_wait_for_event() sits in this loop until an event is recieved. after its recieved, it removes the event and places it into the ALLEGRO_EVENT ev, that will be executed from that point on. After it reaches the end of the loop, if no new events were placed into the queue, it should break out of the loop and hit the render.

As for Edit3: I see your point, but does that mean that i should be placing these things outside of the timer loop the same as the render function?

IE:

do{
//process events
}while(!al_is_event_queue_empty());

//process collision detection if true

//process render if true

Aikei_c

You can do it like this.
Or, you could make up something else. For example, here is the loop I currently use. Names of functions and variables should be self-explanatory.
All your game code is in logic update, input updates are handled in ProcessAppMessages, and rendering is done by the renderer.
And Rest() just calls al_rest().

#SelectExpand
1 while(!m_bDone) 2 { 3 m_timeNow = GetTime(); 4 m_deltaTime = m_timeNow - m_lastRunTime; 5 if (m_deltaTime < m_oneFrameTime) 6 continue; 7 m_lastRunTime = m_timeNow; 8 ProcessAppMessages(); 9 TickEventManager(); 10 m_pGameLogic->VOnUpdate(m_deltaTime); 11 m_pIRenderer->VOnRender(m_deltaTime); 12 m_nextTime = m_timeNow+m_oneFrameTime; 13 m_restTime = m_nextTime-GetTime(); 14 if (m_restTime > 0) 15 Rest(m_restTime); 16 }

EDIT: Your loop however should work just fine :). You can process all your game code if render is true, right before rendering everything.

Mishtiff

Thanks for the help Aikei_c, my game now updates and renders, but it slows down to about ~20-30 frames when it is supposed to be running at 60 fps. I need to figure out how to run the collision detection in a more efficient way. When i remove the collision detection everything runs fine, but once it is back in, it lags it pretty hard.

Oddly enough, when i increase the amount of units that can be tested for each quadrant (before splitting into new leafs) the game runs better. If i continue to break the quadrant up at lower numbers (such as, only allowing ~3-10 units per quadrant before splitting) it runs at awful frames. I need to find a good source code that uses this type of collision detection to see how to run it in a more efficient way.

When i read about quadtree collision detection, it seemed that most people agreed that it was the way to go in regards to efficiency. Weird that i am having such awful lag due to it (considering that its the only code that could possibly tax the program).

pkrcel

I'd suggest you dive in to profiling your code because seems you can't "debug" the issue further just sectioning the code.

Mishtiff

@prkcel As in, Seperating code into different .cpp and .h files? Such as a game.cpp, player.cpp etc?

Trent Gamblin

Sorry, since this thread is getting long can you post your collision detection again? Maybe we can see where the bottleneck is.

EDIT: Mainly the GetObjectsAt and CheckCollisions methods.

pkrcel

I meant profiling as in using a tool (such as the Visual Studio Debugger suite) that might help in identifying where in code you have bottlenecks.

You already know that the collision detection drowns performance, but you don't know which BIT of it.

I'm afraid I can't be of much help here, I haven't profiled code since my old time on TRACE32. :P

On the other hand, structuring your code better would help a lot in sharing & understanding for people which might look upon your code....first of all yourself.

At the very least, a buch of conveniently placed (as in when calling a fnction or traversing a loop iteration) "debug" printf with timestamps might help.

Aikei_c
Mishtiff said:

Oddly enough, when i increase the amount of units that can be tested for each quadrant (before splitting into new leafs) the game runs better.

This is because quadtree also takes time to build every tick. If you specify too low number of units per quadrant, it will have to build more quadrants and will take more time. Depending on your number of actors, some quad trees may actually slow down performance instead of improving it.
Also note, if you have any static objects which never move, you don't need to rebuild a quadtree for them every tick, you need to add them just once. You could even make two quad trees: one for active objects which gets cleared every tick, and the other for static ones.

Mishtiff

EDIT: Mainly the GetObjectsAt and CheckCollisions methods

#SelectExpand
1//GetObjectsAt() 2vector<GameObject*> Quadtree::GetObjectsAt( double x, double y ) 3{ 4 if ( isLeaf ) { 5 return objects; 6 } 7 8 vector<GameObject*> returnedObjects; 9 vector<GameObject*> childObjects; 10 11 if ( !objects.empty() ) 12 returnedObjects.insert( returnedObjects.end(), objects.begin(), objects.end() ); 13 14 for ( int n = 0; n < NodeCount; ++n ) { 15 if ( nodes[n].contains( x, y ) ) { 16 childObjects = nodes[n].GetObjectsAt( x, y ); 17 returnedObjects.insert( returnedObjects.end(), childObjects.begin(), childObjects.end() ); 18 break; 19 } 20 } 21 22 return returnedObjects; 23}

#SelectExpand
1//CheckCollisions 2bool GameObject::CheckCollisions(GameObject *otherObject) 3{ 4 float oX = otherObject->GetX(); 5 float oY = otherObject->GetY(); 6 7 int obX = otherObject->GetBoundX(); 8 int obY = otherObject->GetBoundY(); 9 10 if( x + boundX > oX - obX && 11 x - boundX < oX + obX && 12 y + boundY > oY - obY && 13 y - boundY < oY + obY) 14 return true; 15 else 16 return false; 17}

Hope this gives some insight.

pkrcel said:

1. I meant profiling...
2. structuring your code better would help a lot in sharing & understanding for people which might look upon your code...
3. At the very least, a buch of conveniently placed (as in when calling a fnction or traversing a loop iteration) "debug" printf with timestamps might help.

1a. Now i understand your suggestion. I have tried using std::cout for timestamps, where i call the current time and track how long it takes to get through a loop. Would you like me to place this back in and check how long some loops are taking?

2a. Ill try to clean it up as to help others understand and not get lost. Since it is my code, I understand it fully, but I will be sure to make it more readable for not only you guys, but for others who look at it for learning.

3a. refer to 1a.

Aikei_c said:

1. some quad trees may actually slow down performance instead of improving it...
2. Also note, if you have any static objects which never move, you don't need to rebuild a quadtree for them every tick, you need to add them just once...

1a. I understand what you mean. I do know that my quad tree does not destroy the quadrants that were made in the last loop. This meaning that if it broke itself down into 10 quadrants, on the next iteration, all of the quadrants from before will be cleared, but the leaves and branches are never reset. I Imagine that could cause some unnecessary looping in the tree search (maybe not actually).

2a. Yeah i currently do not add the static units to the tree at all (since im not worried about collisions with them at this moment). The only units that are added to the quad tree are the ships that move.

Trent Gamblin

I don't know what "objects" is in your code, but I assume it's all objects? Your code adds ALL objects repeatedly for every node that x, y is in. That's why smaller divisions gets slower, it's adding all objects multiple times. You need to rewrite the GetObjectsAt method, doesn't make any sense. A quadtree is supposed to track each object and which quad they're in and then add only the objects in a specific section to the values it returns. Whenever an object moves, it's quadrant is adjusted if needed.

Aikei_c

Boost has a nice spatial partitioning module which you can adapt to your needs. It's pretty fast and doesn't even require linking, it's headers only.
http://www.boost.org/doc/libs/1_57_0/libs/geometry/doc/html/geometry/spatial_indexes/introduction.html
http://www.boost.org/doc/libs/1_57_0/libs/geometry/doc/html/geometry/spatial_indexes/rtree_quickstart.html

Mishtiff

I don't know what "objects" is in your code, but I assume it's all objects? Your code adds ALL objects repeatedly for every node that x, y is in. That's why smaller divisions gets slower, it's adding all objects multiple times. You need to rewrite the GetObjectsAt method, doesn't make any sense. A quadtree is supposed to track each object and which quad they're in and then add only the objects in a specific section to the values it returns. Whenever an object moves, it's quadrant is adjusted if needed.

objects refers to anything that is a collide-able object. As of right now, i am only adding ships to the quad-tree. Later ill make other objects collide-able.

As for the GetObjectsAt() method: I did not write any of the quadtree code. I was fortunate enough to find a post where someone had made a adaptive quadtree.
link: http://veendeta.wordpress.com/2012/10/26/algorithm-adaptive-quadtree/
It could be that i do not understand it well enough to use it correctly, but i think i have a pretty good idea. Also, in the comments at the bottom of that page, she talks about creating a function that checks an area around a unit rather than passing a point.

The current usage does work, however. as it breaks down into smaller pieces, each object is in only 1 node. so every node that is higher than the "leaf" node is empty, until it hits the leaf. I did some of the printing for it, and it is returning the correct amount of units that are in the area (unless i'm misunderstanding what you mean).

Aikei_c said:

Boost has a nice spatial partitioning module which you can adapt to your needs.

This seems great, but looking over it has me a bit confused. I'm still learning as much as i can in C++, so some of this seems out of my depth.

Trent Gamblin

Not so sure you were fortunate. When I look at this:

if ( !objects.empty() )
    returnedObjects.insert( returnedObjects.end(), objects.begin(), objects.end() );

You've just added every single object to the list that's returned. Then a little further down, you add every single object again, multiple times. What am I missing?

pkrcel

I can't speak for the implementation, but I GUESS that if it's not a leaf node it should not have objects in itself.

So we're basically looking at a bunch of dead code.

IIF on the other hand, the non-leaf node contains objects...

Trent Gamblin

Ok then, I'm not sure. I can't tell anymore about it without seeing the whole code, but rather than do that I would learn about profiling as pkrcel suggested.

EDIT: The fact that the class is called QuadTree to me implied the whole tree, if it's a node then "Node" would be a better name.

Mishtiff

You've just added every single object to the list that's returned.

right, i see where you are confused. Essentially this code runs through each quadtree level until it hits a level that STILL has objects in it. This may clear it up for you a bit:

#SelectExpand
1void Quadtree::AddObject( GameObject *object ) 2{ 3 if ( isLeaf ) { 4 objects.push_back( object ); 5 bool reachedMaxObjects = ( objects.size() == numObjectsToGrow ); 6 if ( reachedMaxObjects ) { 7 createLeaves(); 8 moveObjectsToLeaves(); 9 isLeaf = false; 10 } 11 return; 12 } 13 14 for ( int n = 0; n < NodeCount; ++n ) { 15 if ( nodes[n].contains( object ) ) { 16 nodes[n].AddObject( object ); 17 return; 18 } 19 } 20 21 objects.push_back( object ); 22} 23 24void Quadtree::createLeaves() 25{ 26 nodes = new Quadtree[4]; 27 nodes[NW] = Quadtree( left, (left+right)/2, top, (top+down)/2, numObjectsToGrow ); 28 nodes[NE] = Quadtree( (left+right)/2, right, top, (top+down)/2, numObjectsToGrow ); 29 nodes[SW] = Quadtree( left, (left+right)/2, (top+down)/2, down, numObjectsToGrow ); 30 nodes[SE] = Quadtree( (left+right)/2, right, (top+down)/2, down, numObjectsToGrow ); 31 32 33} 34 35void Quadtree::moveObjectsToLeaves() 36{ 37 for ( int n = 0; n < NodeCount; ++n ) { 38 for ( unsigned int m = 0; m < objects.size(); ++m ) { 39 if ( nodes[n].contains( objects[m] ) ) { 40 nodes[n].AddObject( objects[m] ); 41 objects.erase( objects.begin() + m ); 42 --m; 43 } 44 } 45 } 46}

In addObjects() we add all objects into the quad tree. if the quadrants hits the maximum units, it subdivides, creating the leaves and clearing itself of any objects in the currently level, but adding them to the next level down.

so in the getObjectsAt() function, every node that is (!isLeaf) will be empty for objects, and even though it seems that it is constantly adding objects, technically the objects in the higher level quadrants are empty.

pkrcel said:

So we're basically looking at a bunch of dead code.

Like i said before, im learning this stuff as i go, so i have no idea how to just pass over the dead code directly to the isLeaf quadrant.

Talking about getObjectsAt() again, ill go through what i think it does, and you can correct me if im wrong.

if the quadrant is a leaf, return all objects in the leaf. else, create two vectors to store data.

if(!objects.empty()) add to returnedObjects. however, this should always be empty because we move objects down the leaves and erase where they were before.

then in the for loop here, we find what node contains x,y and continue to traverse the tree until we find a leaf, and add those objects to the returned objects.

Trent Gamblin

Alright. What is numObjectsToGrow set at? Is that the value that when set low causes slowness? It shouldn't be really low. It'll be faster to check collisions with 10 objects than to loop through a ton of nodes. Also how many objects are there in total (so I don't have to read to find it?)

Mishtiff

no problem trent :)

i currently have numObjectsToGrow set at 20. ive tried different variances, the lower the worse it is, the higher the better (to a point... like 40). total objects currently is 200. i would like to get it to where it can run a maximum of 500. if this is not possible, i am 100% ok with just running 250-300.
But this is all end game, if i can get past this problem. as of now, im running 100-200 ships and its causing my frames to drop to around 30 (from 60)

Trent Gamblin

Well I don't feel like doing a full analysis of the quadtree but I don't think 500 objects is unreasonable at all, depending on system specs a little bit. FWIW in my game I don't even have a full blown quadtree, I just have a grid I put objects into. It's very simple, just divide each area into 100x100 (or whatever) grid array and check objects in [x][y] in the array. In total there are probably up to a couple thousand objects in some areas and it runs on an old netbook and even a Raspberry Pi. So this leaves me questioning the efficiency of the quadtree implementation you're using. Again, I'd have to do a full analysis of it to figure out what's slow OR profile it but since you're the one who already is set up to build the project, you could profile it. What compiler are you using? I've done profiling with GCC-based compilers but I believe it doesn't work well on Windows. I've also used the profiler from Software Verify (which is insanely expensive, but you can get a free trial) which works with GCC/MSVC built code on Windows. If you're not using Windows than GCC-based profiling is easy to set up. If you're using MSVC then Google for profiling for that, I'm sure there are free ways to do it, probably without installing anything extra. I've even heard of free profilers that work on GCC-based code for Windows so Google for that also.

pkrcel

I'm not a spatial indexing expert but there's really something I don't grasp in this quadtree implementation.

Namely, in addobject there's something like this:

void QuadTree::AddObject(GameObject* object){
   if (isLeaf) {
      //ad objs and split quadrant if reached a max obj threshold then..
   return;
   }
   //otherwise:
for (each child node){
//recursively check if node contains object add object to that node and then //in ANY other case??? *** objects.push_back(object) // should we ever end here? I surely hope not }

I agree with Trent that right now we should look into the Quadtree implementation: my impression is that there might be some unnecessary object loading into returned vectors and you end up doing definitely too many collision checks.

Or might be that you're rebuilding the tree from scratch for each object and things get exponentially heavier even for a small set of objects.

But I'm just speculating, eh. :P

Trent Gamblin

Seems that it adds those objects if they don't lie within the quadtree, and then returns them where I mentioned earlier. Check to see if that last line, objects.push_back(object) ever gets run.

Mishtiff

Well I don't feel like doing a full analysis of the quadtree but I don't think 500 objects is unreasonable at all...
...What compiler are you using?

i am currently using visual studio 2012 on windows.
Thinking of it now, i have been so focused on using this quad tree, because i thought that cleaning up my code with it would be more efficient, that i forgot that my old system actually worked much better.

what i did before was create 12 quadrants in the display area and update each ship based on what quadrant they are in. then i would test each ship for collision in their quadrant. the problem with this was that it cause a TON of extra, messy code. but it did work MUCH better. the other thing that made this hard to use was that if every single ship was in the same quadrant, i had no idea what to do to split it up (since i was just using lists, without the adaptive quadtree implemented.)
If you would like to see an example of what i am talking about let me know and ill post the previous code. It sounds like what you are talking about with the 100x100 grid, but Im not sure i fully understand what you mean by it.

pkrcel said:

I'm not a spatial indexing expert but there's really something I don't grasp in this quadtree implementation.

Looking over what you are referring to:
AddObject()
if(isLeaf) put the ship into object list in this leaf, if the max size is hit, create new leaves and move the objects to the leaves while removing them from this tier of the tree.

for(nodecount) this only runs if the last part was false
check which sub node contains the gameobjects point, then add it to that quadrant.

as for the last part:

//in ANY other case??? ***
objects.push_back(object) // should we ever end here? I surely hope not

I have no idea why this is here, and i agree... it should never get to this code. i have it commented out now in my program and it seems not to effect it at all.

Aikei_c

Last comment on the page you linked says there are mistakes in the code and provides a link to a corrected version.

Mishtiff

Yeah aikei, I did see that. I tried to implement his changes, but they caused the program to crash constantly. Essentially the change he did was cause the program to delete all sub nodes on quad.clear(). However when I tried using the code, I would get code block errors. I can redo the changes and tell you exactly what happens if you like.

pkrcel

This is intersting.

Looking at the edits Saduino put in the Quadtree implementation I can't see why you're getting errors, BUT it cleaned the AddObject code and put a needed delete[] for the sub nodes.

Also, you're rebuildng the quadtree each timer tick assuming quad points to the Root node: without that delete[] you end up with unneeded leaves in the tree (and possibily duplicate ones? CreateLeaves just uses new[] each time, is there some kind of memory leak?)

Also, why is this called "adaptive" if it just rebuilds every frame? ...nothing wrong I assume since it shouldn't cost that much rebuilding it, even thou standard memory allocation is kinda slow.

Mishtiff

Pkrcel- that is what I thought as well. I figured that adding the delete [] nodes would be beneficial because you are resetting the tree. The current way it is set up, it never deletes the nodes after the clear... so that means that all of the nodes that were created in the previous are just sitting around for nothing...
when I added the delete, it gave me a code block error. It's my assumption that when it runs through the clear function, it also deletes the top most nodes, which in turn cause the program to crash when it tries to add new items to the top most nodes that have just been deleted (since they are never redeclared). I think you are correct in saying that there could be a memory leak issue.

The only reason, I believe, that it is considered adaptive is because it adapts it's quadrants to the number of units. In her previous posts on her site she created another tree that doesn't change due to units in the area.I understand why youre saying you don't understand why it's considered adaptive since it's constantly clearing and rebuilding...

Aikei_c

I will show you how you can use boost RTree for your purposes. First of all, you need to start using vectors, as I showed you earlier:

#SelectExpand
1struct Vec2 2{ 3 float x,y; 4 5 Vec2(float a_x, float a_y) { x = a_x; y = a_y; } 6 7 Vec2 operator+(const Vec2 &right) const 8 { 9 Vec2 ret; 10 ret.x = x+right.x; 11 ret.y = y+right.y; 12 return ret; 13 } 14 //same for operator-, operator*, operator/ 15 Vec2 operator+(float right) const 16 { 17 Vec2 ret; 18 ret.x = ret.x + right; 19 ret.y = ret.y + right; 20 return ret; 21 } 22 //same for operator-, operator*, operator/ 23 ... 24};

Then, you need to define a rectangle class which contains its lower left and bottom right points, as well as its center and size, just in case:

#SelectExpand
1struct Rect 2{ 3 float size; 4 Vec2 center,topLeft,bottomRight; 5 6 Rect(Vec2 a_center, float a_size) 7 { 8 size = a_size; 9 SetCenter(a_center); 10 } 11 12 SetCenter(const Vec2& newCenter) 13 { 14 center = newCenter; 15 topLeft = center - size/2; 16 bottomRight = center + size/2; 17 } 18};

Now, each of your actors will store its position as a rectangle, along with other things:

class Actor
{
   Rect boundingBox;
   ...
public:
   const Rect& GetBoundingBox() const { return boundingBox; }
   const Vec2& GetPosition() const { return boundingBox.center; }
   void SetPosition(const Vec2& newPos) { boundingBox.SetCenter(newPos); }  
   ...
};

When you move your actor, you set its new position with the SetPosition() method. You could also define a MovePosition() method which will add argument vector to the center of the bounding box, instead of replacing it:

void MovePosition(const Vec2& moveBy) { boundingBox.SetCenter(boundingBox.center+moveBy); }

This is how you can move your actor:

Actor* actor;
Vec2 positionChange(2, 1);
actor->MovePosition(positionChange);

or

actor->MovePosition(Vec2(2,1));

Now you have a bounding box for each of your objects, which should dynamically change every tick. Now we need to register your new classes with boost so they could be used for boost RTree instead of internal boost classes:

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/geometries/register/box.hpp>
#include <boost/geometry/index/rtree.hpp>

//first agrument is the name of your 2D vector class, second - value type used for vector coordinates by your class, and then names of variables or methods used to access x and y coordinates respectively.
BOOST_GEOMETRY_REGISTER_POINT_2D(Vec2,float,cs::cartesian,x,y);

//first argument - name of your rectangle class, then name of your point class and names of your variables or methods used to access to top left and bottom right points of the rectangle respectively
BOOST_GEOMETRY_REGISTER_BOX(Rect,Vec2,topLeft,bottomRight);

Now, let's proceed to RTree. You could create a permanent RTree and then clear() it and insert() actors as needed, however, according to the manual, packing algorithm which involves bulk loading of all objects at the same time, is much more effective, and it is used when using a specific RTree constructor, so we will probably be better off destroying and creating our RTree each tick.

So, somewhere in your code you define RTree and declare a pointer to it. I assume you will store pointers to Actors in the RTree:

namespace bgi = boost::geometry::index;

struct AreRTreeValuesEqual;
struct GetRTreeIndexable;

//8 is the number of actors per node. you could try and specify a different number.
typedef bgi::rtree<Actor*, bgi::quadratic<8>, GetRTreeIndexable, AreRTreeValuesEqual> RTree;

RTree* pRTree = NULL;

I forward declared AreRTreeValuesEqual and GetRTreeIndexable function objects just to show you that you need to at least declare them before defining RTree. GetRTreeIndexable() takes a pointer to Actor (the first argument of the bgi::rtree template specified above) and must return a const reference (`const &`) to an indexable, that is, your bounding box. AreRTreeValuesEqual() should return true if two Actor* values passed are equal or not (I'm not sure if default functors are adequate for pointer comparison, so I decided to write my own implementation just in case). Here it is:

#SelectExpand
1struct AreRTreeValuesEqual 2{ 3 bool operator()(Actor* v1, Actor* v2) const 4 { 5 return v1 == v2; 6 } 7}; 8 9struct GetRTreeIndexable 10{ 11 typedef const Rect& result_type; 12 result_type operator()(Actor* v) const 13 { 14 return v->GetBoundingBox(); 15 } 16};

Now, let's assume you have a vector of actors somewhere:

std::vector<Actor*> actors;

At the start of every tick you do this:

if (pRTree)
   delete pRTree;
pRTree = new RTree(actors.begin(),actors.end());

This will create an RTree of all your objects. Let's proceed to queries. Let's say you want to check if any actors are collided with the current one:

Actor* currentActor;
std::vector<Actor*> results;
//find all actors intersecting with the current one and insert results into the "results" vector:
pRTree->query(bgi::intersects(currentActor->GetBoundingBox()),std::back_inserter(results));

Now, theoretically, if results vector is not empty, then you have collision. However, you will probably get collisions of the actor with itself too, so you need to check if (results.size() > 1) to learn if there is any collisions or not.

EDIT:
Note that since Rect doesn't have a default constructor you will need to specify something like this in the Actor's constructor:

Actor::Actor(whatever) : boundingBox (starting position, size)
{
  ...
}

It might still be a good idea to specify default constructors for the Vec2 and Rect classes, since boost might require them to instantiate blank objects:

struct Vec2
{
   Vec2() { x =0; y = 0; }
};

struct Rect
{
   Rect() {} //maybe specify some starting parameters, but I don't think you will ever need default constructor 
};

Also, you might want to specify a SetSize() method for Rect, but note that you should also recalculate topLeft and bottomRight after changing size.

pkrcel

Ah right, calling delete on the root node would cause issues since you have to explicitly call the constructor again (or if it was static, uhm, what? :P ).

But for how I see it now, calling clear() on your item named quad should not cause issues since it calls delete[] only on the children nodes(as it should, the root node is not part of any array).

EDIT: man, I've read Akei post and now I feel dizzy ;D

EDIT2: I just notice my first paragraph didn't make sense due to bad writing.

Mishtiff
pkrcel said:

But for how I see it now, calling clear() on your item named quad should not cause issues since it calls delete[] only on the children nodes(as it should, the root node is not part of any array).

the code does not crash until after 20 units overall have spawned. I have found that the crash happens after the quadrant runs the clear method. this is directly after the split, so im not sure how to go about solving the problem. NOTE: the tree is set to split at 20 units. so it has to do something with this. I am getting a memory read error at this point, but up until then, i do not receive the error. This must mean that it is during the deletion of the nodes?

Aikei_c said:

I will show you how you can use boost RTree for your purposes.

Wow what a post. I have a ton of appreciation for everything there. Ill deffinately look into using it now. I am positive that I will have several problems still, but this is a huge start. Again, thank you for taking the time to do that!

Question: I am currently using lists as my object holders. Does it matter between that and a vector?

UPDATE: Aikei, it seems ive already messed up boost. during installation somewhere. now im getting a constant error: /Microsoft was unexpected at this time. Ill try to figure it out in the mean time unless you have heard of this.

Aikei_c
Mishtiff said:

Question: I am currently using lists as my object holders. Does it matter between that and a vector?

No. Lists are fine too.

Quote:

UPDATE: Aikei, it seems ive already messed up boost. during installation somewhere. now im getting a constant error: /Microsoft was unexpected at this time. Ill try to figure it out in the mean time unless you have heard of this.

No idea what this could mean. Maybe your boost is installed into a directory which has parentheses in its name (like Program Files (x86)) and you have this path specified somewhere in visual studio? Because visual studio is notorious for having problems with parentheses.
Note that you don't need to link any boost binaries for this, you only need headers.
EDIT: Note by the way, that my rectangles are actually squares, you might want to use width and height instead of a single size parameter, since you probably need actual rectangles, not squares.

pkrcel
Mishtiff said:

the code does not crash until after 20 units overall have spawned. I have found that the crash happens after the quadrant runs the clear method. this is directly after the split, so im not sure how to go about solving the problem. NOTE: the tree is set to split at 20 units. so it has to do something with this. I am getting a memory read error at this point, but up until then, i do not receive the error. This must mean that it is during the deletion of the nodes?

Can't check right now, but this sounds as the qudrant is deleted and THEN the vanished nodes are trying to be accessed (and will likely cause a segfault).

Check if the code clears the nodes array too soon or if you try to access the nodes right after deletition.

Mishtiff
pkrcel said:

Check if the code clears the nodes array too soon or if you try to access the nodes right after deletition.

I feel pretty good about this, all i had to do was sleep on it and i have the answer i believe.

During the deletion, it never sets the topmost of the tree to isLeaf = true; thus, when it returns to the AddObjects during the update loop, it skips over the first part of the addobject and goes to the sub nodes, which were just deleted since we are starting over again! here ill post the code again:

#SelectExpand
1void Quadtree::AddObject( GameObject *object ) 2{ 3 if ( isLeaf ) { 4 objects.push_back( object ); 5 bool reachedMaxObjects = ( objects.size() == numObjectsToGrow ); 6 if ( reachedMaxObjects ) { 7 createLeaves(); 8 moveObjectsToLeaves(); 9 isLeaf = false; //This is where the topmost is set to false and never returned during the clear portion. 10 } 11 return; 12 } 13 14 for ( int n = 0; n < NodeCount; ++n ) { 15 if ( nodes[n].contains( object ) ) { 16 nodes[n].AddObject( object ); 17 return; 18 } 19 } 20 21 //objects.push_back( object ); 22} 23 24void Quadtree::Clear() 25{ 26 objects.clear(); 27 28 if ( !isLeaf ) { 29 for ( int n = 0; n < NodeCount; ++n ) { 30 nodes[n].Clear(); 31 } 32 } 33 delete [] nodes; 34}

Initially, when the tree is made, the topmost for isLeaf = true; however, it breaks down upon hitting the max number of units and changes to false, and never gets reset to true. now i have to figure out how to set it back to true... I may need to make a new function that is something like:

void resetLeaf() {isLeaf = true;}

Update:: This did fix the problem! however, frames still drop to ~20. So still having issues, but it did fix the clear function!

Aikei_c said:

aybe your boost is installed into a directory which has parentheses in its name (like Program Files (x86)) and you have this path specified somewhere in visual studio?

well there is a video i was watching about installing it, and it said i need to run the .\b2 install portion, but i never ran it. so now i am just lost. I tried searching my comp for anything that had boost in it, and i cannot find it anywhere.

I think i will just try to call the headers in VS12 and see if they work. If not, i honestly do not know how to fix what i did.

pkrcel
Mishtiff said:

During the deletion, it never sets the topmost of the tree to isLeaf = true; thus, when it returns to the AddObjects during the update loop, it skips over the first part of the addobject and goes to the sub nodes, which were just deleted since we are starting over again!

Right! Haven't looked at this gotcha....the improved clear() method should reset the Leaf status upon deleting the nodes[] array.

void Quadtree::Clear() {
   objects.clear();
   if ( !isLeaf ) {
      for ( int n = 0; n < NodeCount; ++n ) {
         nodes[n].Clear();
      }
   }
   delete [] nodes;
   isLeaf = true; *** 
}

Even thou, seems performances drop anyway...we'll have to look elsewhere.

Mishtiff
pkrcel said:

Even thou, seems performances drop anyway...we'll have to look elsewhere.

I honestly have no idea where to look now. i guess i could try to see how long it takes for the tree to collect all of the units that are in a node? I feel as though it should be faster than it is... But i am pretty new to a lot of this stuff, so im just happy about where I am now i suppose.

my problem with the Boost addition:

My path file is messed up with the microsoft directx SDK path. here is a link to what the problem is:
http://weblogs.sqlteam.com/mladenp/archive/2008/05/28/Serious-Visual-Studio-2008-install-bug-that-can-break-your.aspx

I have the exact same problem, but i cannot figure out how to get the "" out of the path file. i have tried what he suggested but it didnt work. Im not sure how to edit the path file outside of the .bat file though.

Aikei_c
Mishtiff said:

Im not sure how to edit the path

You can edit path this way (under windows 7 at least):
1) windows logo -> right click on Computer -> Properties -> Advanced system settings -> Environment variables... -> now find Path -> Click Edit

Mishtiff

thank you Aikei, i had no idea where that was. Its all running now!

pkrcel
Mishtiff said:

I honestly have no idea where to look now. i guess i could try to see how long it takes for the tree to collect all of the units that are in a node? I feel as though it should be faster than it is... But i am pretty new to a lot of this stuff, so im just happy about where I am now i suppose.

Well, your code should be profiled as me and Trent suggested, or maybe you can try first to switch to Boost implementation of the r-tree instead of this adaptive quadtree, which should be at least efficinet and robust (the Boost introduction gives times to build the tree and to query the index that shoould very well be under your timing constraints), since usually Boost is. :P

I too think you're hitting the ceiling too fast.

For example how do you handle gfx? are those primitives or bitmap? In the latter case is bitmap drawing held?

Maybe you could setup a git repository for us to look at? I guess your actual code coudl be shared...

Mishtiff
person said:

maybe you can try first to switch to Boost implementation of the r-tree instead

I plan on implementing this now, with much thanks to Aikei for all the help on getting it started. already in the works! :D

person said:

For example how do you handle gfx? are those primitives or bitmap? In the latter case is bitmap drawing held?

Everything is primitives. so, during drawing this is literally it:

Bases:
al_draw_circle(x, y, 50, HomeBase::GetColor(), 2);
al_draw_textf(font18, HomeBase::GetColor(), x, y-75, 0, "P%i: %i", GetPlayerID(), GetScore());

Ships:
al_draw_circle(x, y, boundX, BaseColor, 1);

the program has to render around 200-400, but i dont see that being a problem if they are just primitives...

person said:

Maybe you could setup a git repository for us to look at? I guess your actual code coudl be shared...

of course, would gitHub be a good place to store it?

Aikei_c
Mishtiff said:

would gitHub be a good place

Which reminds me that GitHub has been banned today in Russia for "suicide propaganda", and I'm not joking. ;D.

Mishtiff

thats crazy Aikei!

Im getting around 25 errors now that i have everything implemented. Granted, I may not have everything correct, since i have been up for about and hour or two messing and recoding.

Ill put my stuff up on gitHub once i wake up. No idea whats going on or if i have things in the right place.

Aikei_c

What is the first error you have? It is usually the one which causes everything to crash.
Better go with bitbucket, I won't need to configure tor for that, ;D.

Mishtiff

ok sounds good ill post there instead.

well i closed the program for a little while, and now its running fine... odd? all of the boost errors went away. game runs fine, but the pRTree is not working correctly im sure, since its saying that there are all objects returned as colliding.

I have set the bounding box, not sure why it is returning all as colliding.

EDIT: it does seem to have a problem with: BOOST_GEOMETRY_REGISTER_POINT_2D(Vec2,float,cs::cartesian,x,y);

saying: Name followed by '::' must be a class or namespace name.

something to do with cartesian

Aikei_c

Do you include <boost\geometry\geometry.hpp> (or just boost\geometry) ?

EDIT: If it compiles, though, you should be fine. Visual studio is sometimes wring with its on-the run error detection. Show your code.
EDIT2: Make sure your bounding boxes really don't collide. If all objexts are shown as colliding that's because their bounding boxes are. Check values of topLeft and bottom right corners, even draw them, and make sure they do not collide. This might happen if all of your bounding boxes initialize to zero position or something like that.

pkrcel
Mishtiff said:

the program has to render around 200-400, but i dont see that being a problem if they are just primitives...

There was some debate about that actually. I'd like to see if going the bitmap route may make any difference.

IIRC you come from Mike Geig's tutorial, try to draw your circle into a bitmap (one for base, one for ship) like Mike does for the spaceship at program setup, an then use THAT bitmaps instead of drawing primitives.

Might also use al_hold_bitmap_drawing() when there.

Of course you should solve the Boost rtree implementation before.

And bitbucket is as good as anything else for this matter I guess.

Mishtiff

I have my files up on bitbucket. not sure how to link them, but heres a shot:

https://mishtiff@bitbucket.org/mishtiff/basegame.git

bullet is currently unused, but may be used in the future. i cant figure out how to remove Arial, all it is is the font used.

Thomas Fjellstrom

Try and find the public git address, not your personal read/write address ;)

Mishtiff

Ill be honest im not sure how to find it. I assumed that was the one, since i could get to it without being logged in

pkrcel

might be as simple as bitbucket.org/mishtiff

I'll have a look.

Aikei_c

I haven't looked at it closely, by judging from this line:

al_draw_rectangle(bb.center.x - bb.topLeft.x, bb.center.y - bb.topLeft.y, bb.center.x - bb.bottomRight.x, bb.center.y - bb.bottomRight.y, BaseColor, 1);

You think that topLeft and bottomRight values are relative to the center. They shouldn't be. They should be absolute positions. If all of your bounding boxes are, like, (-15,-15) to (15,15) there is no wonder they all collide...

Also, there is a mess in the Object class. You only need bounding box, that is all. You don't need any additional x, y, boundx, boundy, all this should be represented by your bounding box...

Mishtiff

Yeah this seems to be the correct link i hope!

https://bitbucket.org/mishtiff/basegame/src

sorry if not.

Aikei, I was just following your big post earlier :) i did do some profiling and found that all of my blocks were returning a -1.5 and 1.5 for their topLeft and Bottom right. I assumed it was a mistake i just havent fixed it in my coding yet.

I wasnt aware until then that i needed to save the topLeft and bottomRight Vec2's as their actual position, and not just the bounding position.

Aikei_c
Mishtiff said:

I wasnt aware until then that i needed to save the topLeft and bottomRight Vec2's as their actual position

I however told you that and even provided a method to move your actor:

Aikei_c said:

When you move your actor, you set its new position with the SetPosition() method.

Which means, each time you change position of your object, due to its movement or anything, you use SetPosition(). No x = x+velx and that kind of shit! You only use SetPosition() method to set position, nothing else. And you don't need those boundx, boundy, x, y, etc. You already have your position.

pkrcel

Just for the record, but how do you resolve collision?

I see you get the result vector back but you don't seem to do anything with it apart from checking if it's size is bigger that 1.

Anyway I can't seem to find a reasonable reason (ah!), or at least a not subtle one for a slowdown to happen. (apart from batching draw calls into a single al_draw_prim call of course...but that's not the point here I guess)

I'll have to compile it myself, but I am unable right now....

EDIT: ah btw Aikei is right, your code needs further cleanup...at least for clarity, with this disorder one can't rule out some camouflaged gotcha in the code :P

Mishtiff

@aikei yeah you did, I just have been so busy I haven't been able to spams the time needed to clean up my code. I do plan on talking away the unused x, y, bx, by. Sorry, I just wanted to get my code up so you could see where I am at now.
@pkrcel I thought that by running the check through boost (where I send it the actors location and bounds) that it would return whatever classes with said object.

I will program as much as I can tomorrow before work and post an update!

Edgar Reynaldo

I think what your code could use is a few more functions. You need to separate out functionality and purpose into smaller units that you can then combine. For example, Collision(), Cull(), CheckScore(), things like that.

Mishtiff

@Edgar of course, I just need to start working towards it. I just need to sit down and take the time to figure out how to pass the information around. But splitting my code up so that my game loop isn't a wall of text would be spectacular. As I said just before this, I'm trying to clean my code up, it's just hard to accomplish without messing something else up, or paying the right information. Something I'm learning to do :)

pkrcel
Mishtiff said:

I thought that by running the check through boost (where I send it the actors location and bounds) that it would return whatever classes with said object.

It sure does, but as long as I am not missing something obvious you aren't doing anything to the collided objects, am I wrong?

Mishtiff

Yeah that is correct, I was only checking to see if I had implemented the tree correctly by seeing how many objects were returning as collided

UPDATE: I have fixed a bunch of code! Everything is running as intended. I did have to switch a couple lines, but we can talk about it once i get the code uploaded to gitbucket. Im still having some framerate drop, but its only 10-20 frames at most, so not too bad. It was handling 500 units at about 40 frames. So we just need to see if my collision detection code is too heavy. :)

Still working on cleaning up my code, so i will not be updating it for at least another day or two.

In the mean time, what are the commands to update my remote files with my local ones? I cant seem to find a solid answer (or at least, one that worked when i tried it). Thanks guys! :D

Aikei_c
Mishtiff said:

So we just need to see if my collision detection code is too heavy.

If your objects are rectangles, you probably don't need any other specific collision detection code, apart from the RTree. Also, make your Rects actual retangles, with width and height parameters instead of just size which makes them squares.
or, at the very least, you would run any additional specific collision detection code only for objects returned by RTree in order to calculate the so-called "collision normal", that is, the direction you need to push two objects to get rid of collision.

Quote:

In the mean time, what are the commands to update my remote files with my local ones?

Commit, then push. Depending on the client you use, you probably need to select files first when commiting.

Mishtiff

@Aikei Sounds good. I think i was confused on what boost does, now i understand that boost does insertion, a tree and checks for collision. i thought it only did the first two :) so i also added this to it:

#SelectExpand
1for(iter = objects.begin(); iter != objects.end(); ++iter) 2 { 3 std::vector<GameObject*> results; 4 //find all actors intersecting with the current one and insert results into the "results" vector: 5 pRTree->query(bgi::intersects((*iter)->GetBoundingBox()),std::back_inserter(results)); 6 int size = 0; 7 size = results.size(); 8 if(size <= 1) continue; 9 10 for(int i = 0; i < size; i++) 11 { 12 if(i == 0) continue; 13 if((*iter)->CheckCollisions(results[i])) 14 { 15 results[i]->Collided((*iter)->GetID(), rand() % 10 + -5, rand() % 10 + -5); 16 } 17 } 18 }

This is the version i was using, which wouldnt make sense if the query is returning all actors that intersect each other... ;D.

#SelectExpand
1for(iter = objects.begin(); iter != objects.end(); ++iter) 2 { 3 std::vector<GameObject*> results; 4 //find all actors intersecting with the current one and insert results into the "results" vector: 5 pRTree->query(bgi::intersects((*iter)->GetBoundingBox()),std::back_inserter(results)); 6 int size = 0; 7 size = results.size(); 8 if(size <= 1) continue; 9 10 for(int i = 1; i < size; i++) 11 { 12 results[i]->Collided((*iter)->GetID(), rand() % 10 + -5, rand() % 10 + -5); 13 } 14 }

new code, tell me if that works. Also, i will fix the code for Collided. instead of random movements it will just push in the opposite direction.

Aikei_c

This should work, however you are making an assumption here that the object itself you are checking collision with will always be the first to be returned, which I don't think boost guarantees, since the manual doesn't specify any order in which results are returned.
So you should check something like if (*iter)->GetId() != results[i]->GetID()
(If I understand correctly and this is the unique ID of the current instance of class. If not, you probably could also just check pointers for equality, like results[i] != *iter.)

Mishtiff

@Aikei good point, will implement it now. Ill update this post in a couple hours with the progress

Update: Im not sure what happened, but i hit a critical error. I closed my program to spend some time with the family today, and got an error, so i had to copy and paste the entire program over again. Something about running git. It somehow moved files or something when i went to push them.

Happened on my desktop as well (on accident). all of my folders are gone that i had created... :-/

Anyways, all is well and ill update soon.

Thread #614816. Printed from Allegro.cc