GM:S 1.4 How do I pause the game?

Discussion in 'Programming' started by Dr_Nomz, May 15, 2019.

  1. Dr_Nomz

    Dr_Nomz Member

    Joined:
    Oct 31, 2016
    Posts:
    562
    Is there a way to stop all code from running (So enemies stop moving, timers stop counting down etc) so the game can be paused? And then a way to resume it like nothing happened?
     
  2. BattleRifle BR55

    BattleRifle BR55 Member

    Joined:
    Jun 24, 2016
    Posts:
    947
    There are many ways to achieve it, though there's nothing inherently built in. You could make the room persistent and go to another room. You could have a global pause variable that exits every event if it's set to true. You could deactivate everything. Just to name a few.
     
    Dr_Nomz and Toque like this.
  3. NightFrost

    NightFrost Member

    Joined:
    Jun 24, 2016
    Posts:
    1,801
    If you go with deactivation, it will also be necessary to make a copy of current screen content to surface and draw that to screen while paused. Because when everything is deactivated nothing else will obviously get drawn. And when you deactivate all, you immediately need to follow up with reactivating whichever instance is polling for the unpause button press and drawing the surface to screen. Usually it should be enough to make some obj_pause that does all this stuff.
     
    Bentley likes this.
  4. samspade

    samspade Member

    Joined:
    Feb 26, 2017
    Posts:
    1,931
    I'd second the deactivation idea especially if your objects rely on any built in GM variables (thinks like alarms, image variables, etc) as otherwise it is difficult (or impossible) to stop them from running automatically.

    A basic version would just be instance_deactivate_all, instance_activate_object(subset) where first you deactivate all instances and then you reactive a smaller group of them - which I normally do through inheritance e.g. instance_activate_object(controller_parent).

    A slightly more advance version would be to use a ds_stack, loop through all objects with a with statement, and as you deactivate them, add their ids to an array, then push the array to the ds stack. Then to unpause pop the top array off the stack loop through it and reactivate. This means that you can pause and unpause in layers - e.g. first pause is the game, then you go into a sub menu and pause the main menu, and so on - and you can back up in layers.
     
    Dr_Nomz and Japster like this.
  5. Musehill

    Musehill Member

    Joined:
    Jan 7, 2017
    Posts:
    3
    I have an object called Pausable that is the parent of every object that needs to be paused. It only has one variable called paused. That allows me to easily do "with Pausable paused = true;" Then in important events of each child object, I'll simply wrap everything in an "if !paused { }" conditional. With alarms, I will do "if !paused { /*code */ } else alarm[#] = 1" with "#" being the number of the alarm. You can use a script to do something like:

    with Pausable {
    paused = true;
    old_image_speed = image_speed;
    old_move_speed = speed;
    image_speed = 0;
    speed = 0;
    }

    Then you can reset image_speed and speed to their former values when you unpause.
     
    Dr_Nomz likes this.
  6. Dr_Nomz

    Dr_Nomz Member

    Joined:
    Oct 31, 2016
    Posts:
    562
    I think I like Musehill's idea best, that actually sounds like the best way to do it.

    I'll leave this open so people can post more suggestions, thank you all for your help!
     
  7. BaBiA Game Studio

    BaBiA Game Studio Member

    Joined:
    Jun 20, 2016
    Posts:
    812
    I did it that way in my first game and it was a nightmare. Biggest mistake I ever made. Mainly because of having to handle all of the alarms so that they would not count down. The problem with Musehill's idea of setting the alarm to 1 if the pausable value is set does mean that if you paused the game when your alarm was at 35 (for example), when you unpause it the alarm would have counted all the way down to 1 and would then activate in the next step instead of 35 steps later. May not seem a big thing, but what if you are doing longer alarms? They would all count down and then trigger at the same time when they should not be.

    The best way is to create a pause object and create that to do whatever you would be doing during the pause (whether it is showing a menu, or whatever). And as everyone else in this thread has said, using deactivation and a surface to show the current screen allow you to capture the screenshot to a surface and draw it, deactivate (pause) all other object instances in the room (which would also freeze any alarms until the instance is reactivated), do what you have to do within the pause object, and when you destroy the pause object just destroy the surface and reactivate all the instances in the room. Simple. There are even loads of tutorials on doing exactly this way, if you look for them.
     
    Bentley and nacho_chicken like this.
  8. samspade

    samspade Member

    Joined:
    Feb 26, 2017
    Posts:
    1,931
    I would second BaBia Game Studio. You shouldn't manually pause an object if you are using any built in GM variables or functions. It significantly more work to get right, which means it is also easier to get wrong. GM gives you a way to 'pause' objects. It is to deactivate them. This way is incredibly easy to use and the simplest version can be accomplished in three lines of code, one to deactivate all, one to reactivate the portion you care about, and one to reactivate all when you're done. More complicated versions require only slightly more work (I think my pause stack process is two scripts and maybe 15 lines of code total?).

    You should really only use the 'manual' pause idea if you need the objects to be active in most ways but not in some (e.g. you want them to run behind your menu system but don't want to be able to interact with them).
     
    Dr_Nomz likes this.
  9. JeffJ

    JeffJ Member

    Joined:
    Jun 20, 2016
    Posts:
    316
    As someone who uses the approach of manually handling pause states for all objects, I would like to add some thoughts. While it's true that it is more work and easier to get wrong, it also gives you way more control, and gives you more options than a simple menu pause. An example of this - I do not only use it for actual game pausing, I also use it to do special tricks, like a small 0.2 second freeze frame when a big impact happens. What's cool about this is that I can pause exactly the objects I want to, while letting others run, to have maximum control over not only the functionality, but also the aesthetics (example showcased in this blogpost - just Ctrl+F and search for "freeze").

    Another thing to keep in mind, is with the deactivation method you often need to rely on drawing everything on screen to a surface. Not only can this become a bit more complicated if you have a bunch of post processing shaders running, but more importantly, it doesn't scale well if your game needs to be future proof in terms of supporting higher resolutions in the future. Our game supports 4K out of the box, but in the future, it would also easily adapt to 8K or whatever else it needs to (provided the user's GPU can handle it, obviously). The bigger the native resolution the game runs at, the bigger the surface that needs to be created, which could cause stuttering, visual artifacts or even crashes, because the size of the surface will always have to scale accordingly.
     
    Last edited: May 16, 2019
    Bentley likes this.
  10. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    749
    Just started reading your article. Love it.
     
    JeffJ likes this.
  11. Dr_Nomz

    Dr_Nomz Member

    Joined:
    Oct 31, 2016
    Posts:
    562
    Wait, really? Then what is it? What code, what functions?
     
  12. BaBiA Game Studio

    BaBiA Game Studio Member

    Joined:
    Jun 20, 2016
    Posts:
    812
    Seriously? Try just looking for deactivate or activate and you will find everything in the manual. But here, try instance_deactivate_all and instance_activate_all. And there are also tutorials as I have mentioned already that deal with pausing if you take the time to look for them.
     
  13. samspade

    samspade Member

    Joined:
    Feb 26, 2017
    Posts:
    1,931
    This is the most simplified version.

    Code:
    
    ///pause
    instance_deactivate_all(true);
    instance_activate_object(unpausable);
    
    
    ///unpause
    instance_activate_all();
    
    
    You could also do it so that all pausable objects have the same parent and then it would be:

    Code:
    
    ///pause
    instance_deactivate_object(pausable_parent);
    
    ///unpause
    instance_activate_all();
    
    

    The stack pauser I mentioned is this:

    Code:
    
    ///pause
    var pause_list = ds_list_create();
    with (all) {
        if (object_is_ancestor(object_index, controller_parent)) continue;
        ds_list_add(pause_list, id);
        instance_deactivate_object(id);
    }
    ds_stack_push(pause_stack, pause_list);
    ds_list_destroy(pause_list);
    
    
    ///unpause
    var unpause_list = ds_stack_pop(pause_stack);
    for (var i = 0; i < ds_list_size(unpause_list); i += 1) {
        instance_activate_object(unpause_list[| i]);
    }
    ds_list_destroy(unpause_list);
    
    
    With the appropriate create and clean up events of course. Again this works on having a class that doesn't pause. You could reverse it and instead have a class that pauses as described above.
     
  14. Dr_Nomz

    Dr_Nomz Member

    Joined:
    Oct 31, 2016
    Posts:
    562
    Alright than you for all the help everyone, that looks awesome and I'll play around with it later to see what I can do. :D

    But it mentions something about "serious" glitches when used in the draw event? Could someone elaborate on this? (Just curious) Also any tips for how I should be using this? Do's and Dont's and the like?
     
  15. samspade

    samspade Member

    Joined:
    Feb 26, 2017
    Posts:
    1,931
    I don't actually know. However, you souldn't be using stuff like that in the draw event anyways. The draw events are for drawing not code. For example, draw events run once for every active view. At a minimum this could cause massive lag. It's also possible that due to internal workings of GM, there are other errors.

    In general though, don't put any code in a draw event that isn't draw code.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice