Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » More efficient way to capture mouse position?

This thread is locked; no one can reply to it. rss feed Print
More efficient way to capture mouse position?
Trent Gamblin
Member #261
April 2000
avatar

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
Member #15,776
October 2014

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
Member #261
April 2000
avatar

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
Member #15,776
October 2014

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
Member #261
April 2000
avatar

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
Member #476
June 2000
avatar

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.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Trent Gamblin
Member #261
April 2000
avatar

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

Thomas Fjellstrom
Member #476
June 2000
avatar

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.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Mishtiff
Member #15,776
October 2014

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
Member #476
June 2000
avatar

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).

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Trent Gamblin
Member #261
April 2000
avatar

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
Member #8,592
May 2007
avatar

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
Member #261
April 2000
avatar

You were right.

Mishtiff
Member #15,776
October 2014

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
Member #261
April 2000
avatar

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
Member #15,776
October 2014

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
Member #14,871
January 2013
avatar

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
Member #15,776
October 2014

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
Member #14,871
January 2013
avatar

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
Member #15,776
October 2014

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
Member #14,001
February 2012

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

It is unlikely that Google shares your distaste for capitalism. - Derezo
If one had the eternity of time, one would do things later. - Johan Halmén

Mishtiff
Member #15,776
October 2014

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

Trent Gamblin
Member #261
April 2000
avatar

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
Member #14,001
February 2012

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.

It is unlikely that Google shares your distaste for capitalism. - Derezo
If one had the eternity of time, one would do things later. - Johan Halmén

Aikei_c
Member #14,871
January 2013
avatar

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.



Go to: