Using Views for Start/End Screens Tutorial

../../_images/screen-switch.svg

Views allow you to easily switch “views” for what you are showing on the window. You can use this to support adding screens such as:

  • Start screens

  • Instruction screens

  • Game over screens

  • Pause screens

The View class is a lot like the Window class that you are already used to. The View class has methods for on_update and on_draw just like Window. We can change the current view to quickly change the code that is managing what is drawn on the window and handling user input.

If you know ahead of time you want to use views, you can build your code around the Instruction Screens and Game Over Screens. However, typically a programmer wants to add these items to a game that already exists.

This tutorial steps you through how to do just that.

Change Main Program to Use a View

../../_images/collect-coins-game.png

First, we’ll start with a simple collect coins example: 01_views.py Full Listing

Then we’ll move our game into a game view. Take the code where we define our window class:

class MyGame(arcade.Window):

Change it to derive from arcade.View instead of arcade.Window.

class MyGame(arcade.Window):

This will require a couple other updates. The View class does not control the size of the window, so we’ll need to take that out of the call to the parent class. Change:

super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)

to:

super().__init__()

The Window class still controls if the mouse is visible or not, so to hide the mouse, we’ll need to use the window attribute that is part of the View class. Change:

self.set_mouse_visible(False)

to:

self.window.set_mouse_visible(False)

Now in the main function, instead of just creating a window, we’ll create a window, a view, and then show that view.

Add views - Main method
1
2
3
4
5
6
7
8
def main():
    """ Main method """

    window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
    start_view = GameView()
    window.show_view(start_view)
    start_view.setup()
    arcade.run()

At this point, run your game and make sure that it still operates properly. It should run just like it did before, but now we are set up to add additional views.

Add Instruction Screen

../../_images/instruction_screen.png

Now we are ready to add in our instruction screen as a view. Create a class for it:

class InstructionView(arcade.View):

Then we need to define the on_show method that will be run once when we switch to this view. In this case, we don’t need to do much, just set the background color. If the game is one that scrolls, we’ll also need to reset the viewport so that (0, 0) is back to the lower-left coordinate.

Add views - on_show
    def on_show(self):
        """ This is run once when we switch to this view """
        arcade.set_background_color(arcade.csscolor.DARK_SLATE_BLUE)

        # Reset the viewport, necessary if we have a scrolling game and we need
        # to reset the viewport back to the start so we can see what we draw.
        arcade.set_viewport(0, SCREEN_WIDTH - 1, 0, SCREEN_HEIGHT - 1)

The on_draw method works just like the window class’s method, but it will only be called when this view is active.

In this case, we’ll just draw some text for the instruction screen. Another alternative is to make a graphic in a paint program, and show that image. We’ll do that below where we show the Game Over screen.

Add views - on_draw
    def on_draw(self):
        """ Draw this view """
        arcade.start_render()
        arcade.draw_text("Instructions Screen", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2,
                         arcade.color.WHITE, font_size=50, anchor_x="center")
        arcade.draw_text("Click to advance", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2-75,
                         arcade.color.WHITE, font_size=20, anchor_x="center")

Then we’ll put in a method to respond to a mouse click. Here we’ll create our GameView and call the setup method.

Add views - on_mouse_press
    def on_mouse_press(self, _x, _y, _button, _modifiers):
        """ If the user presses the mouse button, start the game. """
        game_view = GameView()
        game_view.setup()
        self.window.show_view(game_view)

Now we need to go back to the main method. Instead of creating a GameView it needs to now create an InstructionView.

Add views - Main method
1
2
3
4
5
6
7
def main():
    """ Main method """

    window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
    start_view = InstructionView()
    window.show_view(start_view)
    arcade.run()

Game Over Screen

../../_images/game_over_screenshot.png

Another way of doing instruction, pause, and game over screens is with a graphic. In this example, we’ve created a separate image with the same size as our window (800x600) and saved it as game_over.png. You can use the Windows “Paint” app or get an app for your Mac to make images in order to do this yourself.

The new GameOverView view that we are adding loads in the game over screen image as a texture in its __init__. The on_draw method draws that texture to the screen. By using an image, we can fancy up the game over screen using an image editor as much as we want, while keeping the code simple.

When the user clicks the mouse button, we just start the game over.

Add views - Game Over View
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class GameOverView(arcade.View):
    """ View to show when game is over """

    def __init__(self):
        """ This is run once when we switch to this view """
        super().__init__()
        self.texture = arcade.load_texture("game_over.png")

        # Reset the viewport, necessary if we have a scrolling game and we need
        # to reset the viewport back to the start so we can see what we draw.
        arcade.set_viewport(0, SCREEN_WIDTH - 1, 0, SCREEN_HEIGHT - 1)

    def on_draw(self):
        """ Draw this view """
        arcade.start_render()
        self.texture.draw_sized(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2,
                                SCREEN_WIDTH, SCREEN_HEIGHT)

    def on_mouse_press(self, _x, _y, _button, _modifiers):
        """ If the user presses the mouse button, re-start the game. """
        game_view = GameView()
        game_view.setup()
        self.window.show_view(game_view)

The last thing we need, is to trigger the “Game Over” view. In our GameView.on_update method, we can check the list length. As soon as it hits zero, we’ll change our view.

Add views - Game Over View
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
    def on_update(self, delta_time):
        """ Movement and game logic """

        # Call update on all sprites (The sprites don't do much in this
        # example though.)
        self.coin_list.update()

        # Generate a list of all sprites that collided with the player.
        coins_hit_list = arcade.check_for_collision_with_list(self.player_sprite, self.coin_list)

        # Loop through each colliding sprite, remove it, and add to the score.
        for coin in coins_hit_list:
            coin.remove_from_sprite_lists()
            self.score += 1

        # Check length of coin list. If it is zero, flip to the
        # game over view.
        if len(self.coin_list) == 0:
            view = GameOverView()
            self.window.show_view(view)