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

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

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?

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

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

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; 
      }
   }

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

Elias
Member #358
May 2000

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.

--
"Either help out or stop whining" - Evert

Mishtiff
Member #15,776
October 2014

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

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.

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

pkrcel
Member #14,001
February 2012

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.

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

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

....a cliffhanger? :P

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

Trent Gamblin
Member #261
April 2000
avatar

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

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
Major Reynaldo
May 2007
avatar

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

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

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

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

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

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

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.

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

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

Try this code.

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

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

It's probably your code, or your hardware.

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

pkrcel
Member #14,001
February 2012

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.

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

Thomas Fjellstrom
Member #476
June 2000
avatar

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.

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



Go to: