Menu system draws "choice highlight" with delay

P

PantsOfAwesome

Guest
I have tried to add a menu system that draws when the player dies in my game, but when choosing an option, the highlighted option's highlight appears with a slight delay and I have no idea why. Here is the death object obj_death.
Here is the create event code:
Code:
depth = -1;
choice = 0;
move_choice = 0;
Here is the step event code:
Code:
key_up = keyboard_check_pressed(vk_up);
key_down = -keyboard_check_pressed(vk_down);
key_choose = keyboard_check(vk_space);
move_choice = key_up + key_down;
choice += move_choice;
if(choice < 0)
choice = 1;
if(choice > 1)
choice = 0;
And here is the draw event code:
Code:
draw_set_alpha(0.25);
draw_set_color(c_black);
draw_rectangle(0,0,room_width,room_height,false);
draw_set_halign(fa_center);
draw_set_valign(fa_middle);
draw_set_alpha(1);
draw_set_color(c_white);
draw_set_font(fnt_death_large);
draw_text(room_width/2, room_height/2, "YOU DIED!");
draw_set_font(fnt_death_small);
draw_text(room_width/2, room_height/2 +50, "CONTINUE");
draw_text(room_width/2, room_height/2 +100, "QUIT");
if(choice = 0)
draw_rectangle(room_width/2-80, room_height/2+34, room_width/2+15*4+16, room_height/2+64,true);
if(choice = 1)
draw_rectangle(room_width/2-49, room_height/2+84, room_width/2+15*2+16, room_height/2+114,true);
I know the draw code is a bit stupidly made but I was just playing with it untill I got the sizes and positions that I wanted. By the way, I wanted to know if anyone knows a way to make the rectangle drawn stay semi transparent while the text stays as it is.
Thank you in advance!
 
P

PantsOfAwesome

Guest
While I would do the selection differently, the delay might be caused by the keyboard_check() which returns true until you let go of the space-bar. Instead, try keyboard_check_pressed(vk_space).
Thank you for the reply!, but the problem has nothing to do with the space button, since as of now, it doesnt really do anything but exist.
Could you maybe advise me on how you would do the selection? I was just trying to find solutions on my own, and I'd like to see if your method works better. (if I'm not to lazy to check)
 

Phil Strahl

Member
Thank you for the reply!, but the problem has nothing to do with the space button, since as of now, it doesnt really do anything but exist.
Could you maybe advise me on how you would do the selection? I was just trying to find solutions on my own, and I'd like to see if your method works better. (if I'm not to lazy to check)
Usually, I have a persistent control-object that just handles the input which I check for a certain flag. The reason for this is that in this controller object I can check for keyboard input as well as controller input or different keys.

So a simple step-code in this controller object would look someehat similar to yours:
Code:
  key_up  = keyboard_check_pressed(vk_up);
  key_down = -keyboard_check_pressed(vk_down);
  key_choose = keyboard_check_pressed(vk_space);
  
   // reset flags on every step:
   up  = false;
   down  = false;
   confirm = false;
  
   if (key_up)
    up = true;
   if (key_down)
    down = true;
   if (key_choose)
    confirm = true;
So if any of these conditions is met for the current frame, a flag in my controller object is set to "true", e.g. for "up". Currently this looks a bit more cumbersome than necessary, but in the long run, changing or adding buttons for the whole game can easiy be done there.

Now with this controller object present, your step code in the selection-object would look like this

Code:
  if (global.ctrl_input.up)
   {
    choice--;
   }
   else if (global.ctrl_input.down)
   {
    choice++;
   }
  
   // wrap around
   var selection_highest = 1; // how many selection items you have
  
   if (choice < 0)
   {
    choice = selection_highest
   }
   if (choice > selection_highest)
   {
    choice = 0;
   }
If I want now to confirm the current choice, I would have a do_choice_confirm flag which is set in a similar way:

Code:
  if (global.ctrl_input.confirm)
   {
    do_choice_confirm = true;
   }
  
    ...
  
   // later in your step code you can handle the selected choice like this:
   if (do_choice_confirm)
   {
    switch (choice)
      {
      case 0:
         {
          // option 1 is selected
          
            // do stuff
         } break;
         case 1:
         {
          // the other option is selected
         } break;
      }
    
      do_choice_confirm = false; // so that this block only gets run once
   }

Now for drawing a semi-transparent black screen, I'd make use of a surface. If you're not familiar with them, read up on them in the manual (https://docs.yoyogames.com/source/dadiospice/002_reference/surfaces/index.html), they can be really useful. The only downside is that they might be destroyed (e.g. when switching windowed/fullscreen mode), so you constantly have to make sure they still exists. Because I try to keep my draw event as clean as possible, I check for a surface in the step event:

Code:
  if (!surface_exists(surf_black)
   {
    surf_black = surface_create(32, 32); // we will stretch the surface to fill the screen later
    
      // now we activate the surface
      surface_set_target(surf_black)
      draw_clear_alpha(c_white, 1); // fills it with obaque whiteness. why white? because we can tint it with any color, if we want to
      surface_reset_target(); // resets drawing back to the application surface
   }
Now in the draw event, we just need to display the black surface semi-transparently and draw the text on top:

Code:
  if (surface_exists(surf_black)
   {
    draw_surface_stretched_ext(surf_black, 0, 0,
      screen_width, screen_height, // or if you're using views: view_wview[view_current], view_hview[view_current]
                                c_black, // or any other color
                                .5 // or any other alpha);
   }
  
   // text
   draw_set_alpha(1);
   draw_set_color(c_white);
  
  draw_set_halign(fa_center);
  draw_set_valign(fa_middle);
  
   draw_set_font(fnt_death_large);
   draw_text(room_width/2, room_height/2, "YOU DIED!");
  
   draw_set_font(fnt_death_small);
   draw_text(room_width/2, room_height/2 + 50, "CONTINUE");
   draw_text(room_width/2, room_height/2 + 100, "QUIT");
  
   if (choice == 0)
   {
    // you might want to clean this up ;)
    draw_rectangle(room_width/2-80, room_height/2+34, room_width/2+15*4+16, room_height/2+64,true);
   }
   else
   {
    draw_rectangle(room_width/2-49, room_height/2+84, room_width/2+15*2+16, room_height/2+114,true);
   }
Phew! I hope this all made sense -- and works, since I didn't test it. I hope you get the idea. Still I don't know about any slight delay when drawing the rectangles... Maybe the room speed is just too low?
 
P

PantsOfAwesome

Guest
Usually, I have a persistent control-object that just handles the input which I check for a certain flag. The reason for this is that in this controller object I can check for keyboard input as well as controller input or different keys.

So a simple step-code in this controller object would look someehat similar to yours:
Code:
  key_up  = keyboard_check_pressed(vk_up);
  key_down = -keyboard_check_pressed(vk_down);
  key_choose = keyboard_check_pressed(vk_space);

   // reset flags on every step:
   up  = false;
   down  = false;
   confirm = false;

   if (key_up)
    up = true;
   if (key_down)
    down = true;
   if (key_choose)
    confirm = true;
So if any of these conditions is met for the current frame, a flag in my controller object is set to "true", e.g. for "up". Currently this looks a bit more cumbersome than necessary, but in the long run, changing or adding buttons for the whole game can easiy be done there.

Now with this controller object present, your step code in the selection-object would look like this

Code:
  if (global.ctrl_input.up)
   {
    choice--;
   }
   else if (global.ctrl_input.down)
   {
    choice++;
   }

   // wrap around
   var selection_highest = 1; // how many selection items you have

   if (choice < 0)
   {
    choice = selection_highest
   }
   if (choice > selection_highest)
   {
    choice = 0;
   }
If I want now to confirm the current choice, I would have a do_choice_confirm flag which is set in a similar way:

Code:
  if (global.ctrl_input.confirm)
   {
    do_choice_confirm = true;
   }

    ...

   // later in your step code you can handle the selected choice like this:
   if (do_choice_confirm)
   {
    switch (choice)
      {
      case 0:
         {
          // option 1 is selected
        
            // do stuff
         } break;
         case 1:
         {
          // the other option is selected
         } break;
      }
  
      do_choice_confirm = false; // so that this block only gets run once
   }

Now for drawing a semi-transparent black screen, I'd make use of a surface. If you're not familiar with them, read up on them in the manual (https://docs.yoyogames.com/source/dadiospice/002_reference/surfaces/index.html), they can be really useful. The only downside is that they might be destroyed (e.g. when switching windowed/fullscreen mode), so you constantly have to make sure they still exists. Because I try to keep my draw event as clean as possible, I check for a surface in the step event:

Code:
  if (!surface_exists(surf_black)
   {
    surf_black = surface_create(32, 32); // we will stretch the surface to fill the screen later
  
      // now we activate the surface
      surface_set_target(surf_black)
      draw_clear_alpha(c_white, 1); // fills it with obaque whiteness. why white? because we can tint it with any color, if we want to
      surface_reset_target(); // resets drawing back to the application surface
   }
Now in the draw event, we just need to display the black surface semi-transparently and draw the text on top:

Code:
  if (surface_exists(surf_black)
   {
    draw_surface_stretched_ext(surf_black, 0, 0,
      screen_width, screen_height, // or if you're using views: view_wview[view_current], view_hview[view_current]
                                c_black, // or any other color
                                .5 // or any other alpha);
   }

   // text
   draw_set_alpha(1);
   draw_set_color(c_white);

  draw_set_halign(fa_center);
  draw_set_valign(fa_middle);

   draw_set_font(fnt_death_large);
   draw_text(room_width/2, room_height/2, "YOU DIED!");

   draw_set_font(fnt_death_small);
   draw_text(room_width/2, room_height/2 + 50, "CONTINUE");
   draw_text(room_width/2, room_height/2 + 100, "QUIT");

   if (choice == 0)
   {
    // you might want to clean this up ;)
    draw_rectangle(room_width/2-80, room_height/2+34, room_width/2+15*4+16, room_height/2+64,true);
   }
   else
   {
    draw_rectangle(room_width/2-49, room_height/2+84, room_width/2+15*2+16, room_height/2+114,true);
   }
Phew! I hope this all made sense -- and works, since I didn't test it. I hope you get the idea. Still I don't know about any slight delay when drawing the rectangles... Maybe the room speed is just too low?
Thank you so much for your help! I'll definitely use this later on. But I just noticed that when I die and enter the death menu the miniFPS and avgFPS go down drastically (sometimes somehow becoming minuses), could the drawing be messing with the games fps?
 

Phil Strahl

Member
Thank you so much for your help! I'll definitely use this later on. But I just noticed that when I die and enter the death menu the miniFPS and avgFPS go down drastically (sometimes somehow becoming minuses), could the drawing be messing with the games fps?
Could be very much so, draw operations are pretty expensive, that's why using surfaces makes sense. Also, try to keep any calculations away from the Draw event and do them in the step event, so that you only read variables.

Using the Debugger is also a great way to find out what functions in your code take up the most time each frame. For that you need to launch the game via F6. In the Debugger window, right click on the title bar of a pane to open up a drop-down menu of different types. There, select "Profile".
2016-07-19 00_36_25-GlassPanelForm.png

Now if you click the big black dot on the left it turns red, indicating that the profiler is recording each function call at every step until you hit the button again or pause the debugging. You can expand each event and really drill down to the one function that's taking the longest:

2016-07-19 00_36_56-GlassPanelForm.png
In this example, it's somewhere in the Step event of obj_ctrl_color.
 
P

PantsOfAwesome

Guest
Could be very much so, draw operations are pretty expensive, that's why using surfaces makes sense. Also, try to keep any calculations away from the Draw event and do them in the step event, so that you only read variables.

Using the Debugger is also a great way to find out what functions in your code take up the most time each frame. For that you need to launch the game via F6. In the Debugger window, right click on the title bar of a pane to open up a drop-down menu of different types. There, select "Profile".
View attachment 1257

Now if you click the big black dot on the left it turns red, indicating that the profiler is recording each function call at every step until you hit the button again or pause the debugging. You can expand each event and really drill down to the one function that's taking the longest:

View attachment 1259
In this example, it's somewhere in the Step event of obj_ctrl_color.
Thank you again for all your help! So I did that (it slowed my pc quite a bit) and the problem is definitely the draw event, especially the text. So I'll try your suggestion for the background since it might help solve the problem, and I wanted to ask if you know of any method for the text, which for some reason seems to be the function that takes he most time. And by the way could the problem just be cause of my computer? It is'nt the fastest computer in the world but it's supposed to be okay (I think)
[EDIT] I switched the draw with a surface, and now it doesnt draw anything...not sure what the problem is but the fps still goes down, which means (I think) that the real problem is the text
 
Last edited by a moderator:
Top