Python Forum
[PyGame] adding menu system and states (part 9)
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] adding menu system and states (part 9)
#1
Back to part 8

This section focuses on taking the pre-existing tutorial series and adding a state machine to it. Because the code is structured with numerous modules it is easier to just post the links to the branch instead of posting the code snippets here. Each branch is a time snippet of each modification done in this tutorial. It is important to know that each link on this tutorial is linking to a directory structure that is not all the same as the previous link. Each link is a snapshot of each change (AKA a branch) based on what we are describing. The first link is set to compare window, but every thereafter is set the root of the snapshot code (branch). You need to select "compare" button to see the changes. If you want to see the comparison select the "compare" button under clone and download button to see the list of changes from the last one. Or if you want to navigate the code and file structure click the files or directories. If you want to download and run each branch you need to switch to each branch after downloading to run each branch. This is going to require some very basic knowledge of git. If you have any trouble feel free to post a question on the forums.


In this tutorial we have a state machine example. That can be applied to our game (or any game). However it is easier if you start with that as your boilerplate code. But we can work backwards from this game and add the state machine. Before you start this section, you should know this example in and out. You should understand that first and not just be arbitrarily pasting code in if you do not understand it.

Converting to a state machine

In our game series at this point control.py is the main file that handles the game. Everything else is imported into it. This is our one and only state in this game. As the game immediately starts the game, instead for example of a menu system. control.py is also our boilerplate code such as creating the clock that our state machine Control class handles. So we are going to split this up into two different things: Control and game state. Most of everything in control.py is the game state.

The easiest way to handle this is to rename this file game.py and create a whole new control.py with main game loop by copying it from the state machine tutorial. We are stealing the entire control class from that example and copying to our game's control.py.

There are a few modifications to the state machine's tutorial code because we have a pre-existing game structure already:
  1. prepare.py has preparations so there is no need to add a settings dictionary with the screen size and such because our prepare module has it already. Instead we just import prepare and set it to prepare.WINDIZE in the Control class. Thus we remove the settings arguments to Control class from the state machine example.
  2. we put State superclass in tools module we need to prefix all occurrences of State with tools.States.
  3. The menu state is in its own module as well, but is fairly close the the tutorial with the exception of a label to show you to click anywhere to switch to the game state.
  4. added FPS to prepare instead of hard coded in control class for delta time
  5. we moved all the meat of the game to the game state Game class. player = player.Player(prepare.SCREEN_RECT) became self.player = player.Player(prepare.SCREEN_RECT) in the dunder init method as well as everything else to be able to reference it across the entire Game class easily. The event related content was put into the get_event method, all content in the main game loop was put into the update method, and the draw related content was put into the draw method.

You can see every little modification from the compare list: (green = added, red = removed) Red is basically i selected this code and hit delete. Green is basically i pasted this code in its place.
https://github.com/metulburr/ShooterGame...te_machine

At this point we have added two states: a menu red screen and the game state itself. You can expand on this further, but the basics are there.

Organizing states

Before we move on we are going to organize this code even further again. If there are to be a lot of states, it is better to organize states together by themselves. This is better than having one directory with all source code files in one. It better organizes when you need to find something.

https://github.com/metulburr/ShooterGame...ing_states

Here, all i did was move the two states game.py and menu.py to a new directory called states. Because of this file structure change, the imports also had to be changed. You can see the modifications as following:
https://github.com/metulburr/ShooterGame...s?expand=1


Adding menu states
A menu state is nothing more than a selection of these states from another state. You are essentially selecting from this dictionary
state_dict = {
    'menu': menu.Menu(),
    'game': game.Game()
}
However normally would be things like Play, Options, Quit, etc. Each of these could have sub states themselves, etc. How you select these selections doesnt really matter. It can be buttons, text with Pygame rects, etc. In our example we will be using the latter.

In a nutshell: Each option would have a label text with rect the size of its text for collision detection. When one is selected that state is bought to the active status in the control class. If its a single state with no sub states to itself, that state would have the next attribute back to the menu state to re-select again. At any point you can change the states next attribute to modify the state based on situations.

This adds quite a bit of code. It is modular in terms it can be added to and expanded into sub menu systems.
https://github.com/metulburr/ShooterGame...enu_system

When you start this new branch up with its additions, you will see a menu in the red screen now. It can be selected by mouse or keys. There is only two options play or quit. Quit will end the program, while play will start the game.

Everything is currently in States class in the tools module because if you have multiple menu states with sub menus they all need to have access to the menu managing methods. This will be further organized later. But for now lets go over what these actually do. Each method in States has one task. It is defined by the method name. mouse_hover_sound handles the sound when the mouse hovers over it, mouse_menu_click handles mouse collision, select_option handles the actual selection of the menu, pre_render_options handles the animation of the selected and deselected options (how the selected option is larger and different color), and change_selected_option handles the index change of the menu options. All in all this is the menu managing system.

menu.py has been modified to handle specific handling of its states menu in its dunder init method. In this case it is play or quit. If you had an Options selection for example. The separate Options state would have different selections in its menu system. Such as audio, graphics, etc. So lets add an Options state to see how modular the states class is.

The code in menu.py relating to drawing, updating, and the events will soon be moved to a MenuManager class. But for now lets go over it. The draw section handles the positioning of the menu on the screen, and the type of font and size it will be based on if it is selected. The event section handles the increment or decrement of the selected option. This is relating to the code in tools.State. As said before this is fairly sloppy and will be corrected in cleanup later on.

https://github.com/metulburr/ShooterGame...tions_menu

Here we add another state Option to the main menu while in itself is a sub menu. It does not but go back to the main menu though as we have not yet made an audio, graphics, or test state. So hence all selections have to lead somewhere and i lead them back to the main menu. Note how the self.options list is the visible text, while the self.next_list the list of the key to Controls dictionary associated with that state in related to its index with each other. This is the only thing that will change in relation the menu states with sub menu states. You do not need to modify the code in States for handling the menu system unless you are organizing it better.

Anytime you want to add a menu state you can replicate this process. Copy a menu state file, rename it, rename the class, and change the options attribute and next_list according to that states menu selections.

Make sure that the length of the options list is the same as the next_list. Each index corresponds to what is in the other list. In the main menu this is the only exception. If at any time the next_list index is short, it will quit the program. in menu.py quit selection has no state, so it is coded to quit the program when there is no next_list index for the options list.

Adding game over state and restart
In this we move the game over screen to a new state and allow the player to restart the game state after the game is over.

The modifications to this are here
https://github.com/metulburr/ShooterGame..._game_over

In this state all it does is produce a game over label and wait for a keydown or mouse button to go back to the main menu.

For the restart method the player class and enemies class had to reset stats. The players health had to be regenerated, player.dead set to False, and reset enemies position. In each of these classes a new method was created for restart() for specifically restarting the game and resetting the stats to do that.

Cleanup
Now we are going to clean this code up more.

The entire comparison is as following:
https://github.com/metulburr/ShooterGame...after_menu

The States class is filled with menu methods. As well as the menu states are filled with redundant code. We are going to change these by creating a MenuManager class and inputting all menu related content in tool.States in this MenuManager class and put all menu states code redundant in MenuManager. We are just going to cut and paste from tools.State the menu content and paste it in this class.

As for the menu states themselves. The duplicate event code, update code, and draw code across all menu states is removed and put into MenuManager class and replaced with calls to a method that runs the same code. The MenuManager class will be inherited by only the menu state classes. So now the menu states are inherited by both tools.States and MenuManager classes. the individual draw content related to the menu has been cut and pasted into MenuManager.draw_menu() method. The menu related update calls are now in update_menu() method and called directly from the menu. The events related to the menu are now in get_event_menu() and called from the event method in the menu states.

The States class is then left with only 7 lines of code ready to be filled with more later on.

At this point we have cleaned up the States class by creating a MenuManager class. We cleaned up each menu state by putting redundant content from each state into MenuManager class.


As you can see the game itself is quite bland and simple. But adding states adds pizazz.
Recommended Tutorials:
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyGame] Adding player effects (part 4) metulburr 0 6,069 Oct-09-2016, 08:11 PM
Last Post: metulburr

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020