10. Graphical User Interface and Game Development

Graphical User Interface and Game Development

GUI based on tkinter module

GUI is the abbreviation of Graphical User Interface. Anyone who has used a computer should be familiar with the graphical user interface, so there is no need to go into details here. Python’s default GUI development module is tkinter (named Tkinter in versions before Python 3). From this name, you can tell that it is based on Tk. Tk is a toolkit that was originally designed for Tcl and was later ported. Into many other scripting languages, it provides cross-platform GUI controls. Of course, Tk is not the latest and best choice, and it does not have particularly powerful GUI controls. In fact, developing GUI applications is not what Python is best at. If you really need to use Python to develop GUI applications, wxPython, PyQt, PyGTK, etc. Modules are a good choice.

Basically using tkinter to develop GUI applications requires the following 5 steps:

  1. Import what we need in the tkinter module.
  2. Create a top-level window object and use it to host the entire GUI application.
  3. Add GUI components on the top-level window object.
  4. The functions of these GUI components are organized through code.
  5. Enter the main event loop (main loop).

The following code demonstrates how to use tkinter to make a simple GUI application.

import tkinter
import tkinter.messagebox


def main():
    flag = True

    # Modify the text on the label
    def change_label_text():
        nonlocal flag
        flag = not flag
        color, msg = ('red', 'Hello, world!')\
            if flag else ('blue', 'Goodbye, world!')
        label.config(text=msg, fg=color)

    # confirm exit
    def confirm_to_quit():
        if tkinter.messagebox.askokcancel('Warm reminder', 'Are you sure you want to exit?'):
            top.quit()

    #Create top-level window
    top = tkinter.Tk()
    # Set window size
    top.geometry('240x160')
    #Set window title
    top.title('Mini Game')
    #Create a label object and add it to the top-level window
    label = tkinter.Label(top, text='Hello, world!', font='Arial -32', fg='red')
    label.pack(expand=1)
    #Create a container for buttons
    panel = tkinter.Frame(top)
    # Create a button object and specify which container to add it to. Bind the event callback function through the command parameter.
    button1 = tkinter.Button(panel, text='Modify', command=change_label_text)
    button1.pack(side='left')
    button2 = tkinter.Button(panel, text='Exit', command=confirm_to_quit)
    button2.pack(side='right')
    panel.pack(side='bottom')
    # Start the main event loop
    tkinter.mainloop()


if __name__ == '__main__':
    main()

It should be noted that GUI applications are usually event-driven. The reason to enter the main event loop is to monitor the occurrence of various events such as mouse and keyboard and execute the corresponding code to process the events, because events will continue to occur. So such a loop needs to be running all the time waiting for the next event to occur. On the other hand, Tk provides three layout managers for the placement of controls. Controls can be positioned through the layout managers. These three layout managers are: Placer (the developer provides the size and placement of the control) , Packer (automatically fills controls into appropriate positions) and Grid (places controls based on grid coordinates), which will not be described here.

Use Pygame for game development

Pygame is an open source Python module specifically used for the development of multimedia applications (such as video games), which includes support for images, sounds, videos, events, collisions, etc. Pygame is built on SDL, which is a set of cross-platform multimedia development libraries implemented in C language and is widely used in the development of games, simulators, players, etc. Pygame allows game developers to no longer be bound by the underlying language and can focus more on the functions and logic of the game.

Now let’s complete a simple game. The name of the game is “Big Ball Eats Small Ball”. Of course, completing this game is not the focus. Learning to use Pygame is not the focus either. The most important thing is that we must understand how to use the previous steps in the process. Explain object-oriented programming and learn to use this programming idea to solve real-life problems.

Create a game window
import pygame


def main():
    # Initialize imported modules in pygame
    pygame.init()
    # Initialize the window for display and set the window size
    screen = pygame.display.set_mode((800, 600))
    #Set the title of the current window
    pygame.display.set_caption('Big ball eats small ball')
    running=True
    # Start an event loop to process the events that occur
    while running:
        # Get events from the message queue and process the events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running=False


if __name__ == '__main__':
    main()
Drawing in the window

You can draw on the window through the function of the draw module in pygame. The graphics that can be drawn include: lines, rectangles, polygons, circles, ellipses, arcs, etc. It should be noted that the screen coordinate system sets the upper left corner of the screen as the coordinate origin (0, 0). To the right is the positive direction of the x-axis, and down is the positive direction of the y-axis. In the representation When positioning or setting size, our default unit is pixels. The so-called pixel is a point on the screen. You can use image browsing software to try to enlarge a picture several times to see these points. The color representation in pygame uses the three-primary color representation method, that is, the RGB value of the color is specified through a tuple or list. Each value is between 0 and 255, because each primary color uses an 8-bit (bit) To represent the value, the three colors are equivalent to a total of 24 bits, which is often called the “24-bit color representation”.

import pygame


def main():
    # Initialize imported modules in pygame
    pygame.init()
    # Initialize the window for display and set the window size
    screen = pygame.display.set_mode((800, 600))
    #Set the title of the current window
    pygame.display.set_caption('Big ball eats small ball')
    # Set the background color of the window (the color is a tuple composed of the three primary colors of red, green and blue)
    screen.fill((242, 242, 242))
    # Draw a circle (the parameters are: screen, color, center position, radius, 0 means filled circle)
    pygame.draw.circle(screen, (255, 0, 0,), (100, 100), 30, 0)
    # Refresh the current window (the rendering window will present the drawn image)
    pygame.display.flip()
    running=True
    # Start an event loop to process the events that occur
    while running:
        # Get events from the message queue and process the events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running=False


if __name__ == '__main__':
    main()
Loading images

If you need to load an image directly into the window, you can use the function of the image module in pygame to load the image, and then render the image through the blit method of the previously obtained window object. The code is as follows.

import pygame


def main():
    # Initialize imported modules in pygame
    pygame.init()
    # Initialize the window for display and set the window size
    screen = pygame.display.set_mode((800, 600))
    #Set the title of the current window
    pygame.display.set_caption('Big ball eats small ball')
    # Set the background color of the window (the color is a tuple composed of the three primary colors of red, green and blue)
    screen.fill((255, 255, 255))
    #Load the image by the specified file name
    ball_image = pygame.image.load('./res/ball.png')
    # Render the image on the window
    screen.blit(ball_image, (50, 50))
    # Refresh the current window (the rendering window will present the drawn image)
    pygame.display.flip()
    running=True
    # Start an event loop to process the events that occur
    while running:
        # Get events from the message queue and process them
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running=False


if __name__ == '__main__':
    main()
Achieve animation effects

When it comes to the word animation, everyone is familiar with it. In fact, to achieve animation effects, the principle itself is very simple, which is to play discontinuous pictures continuously. As long as a certain number of frames per second is reached, then it can be done Produce a smoother animation effect. If you want the ball in the above code to move, you can use a variable to represent the ball’s position, modify the ball’s position in the loop, and then refresh the entire window.

import pygame


def main():
    # Initialize imported modules in pygame
    pygame.init()
    # Initialize the window for display and set the window size
    screen = pygame.display.set_mode((800, 600))
    #Set the title of the current window
    pygame.display.set_caption('Big ball eats small ball')
    # Define variables to represent the position of the ball on the screen
    x, y = 50, 50
    running=True
    # Start an event loop to process the events that occur
    while running:
        # Get events from the message queue and process them
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running=False
        screen.fill((255, 255, 255))
        pygame.draw.circle(screen, (255, 0, 0,), (x, y), 30, 0)
        pygame.display.flip()
        # Change the position of the ball every 50 milliseconds and refresh the window
        pygame.time.delay(50)
        x, y = x + 5, y + 5


if __name__ == '__main__':
    main()
Collision detection

Usually there will be many objects in a game, and “collision” between these objects is inevitable, such as a cannonball hitting a plane, a box hitting the ground, etc. Collision detection is a crucial issue that must be dealt with in most games. The sprite (animated sprite) module of pygame provides support for collision detection. We will not introduce the functions provided by the sprite module here. Because it is actually very simple to detect whether two small balls collide. You only need to check whether the distance between the centers of the balls is less than the sum of the radii of the two balls. In order to create more small balls, we can process mouse events to create small balls with random colors, sizes and moving speeds at the location where the mouse is clicked. Of course, to do this, we can use what we have learned before Apply object-oriented knowledge.

from enum import Enum, unique
from math import sqrt
from random import randint

import pygame


@unique
classColor(Enum):
    """color"""

    RED = (255, 0, 0)
    GREEN = (0, 255, 0)
    BLUE = (0, 0, 255)
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    GRAY = (242, 242, 242)

    @staticmethod
    def random_color():
        """Get a random color"""
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)
        return (r, g, b)


class Ball(object):
    """ball"""

    def __init__(self, x, y, radius, sx, sy, color=Color.RED):
        """Initialization method"""
        self.x = x
        self.y = y
        self.radius = radius
        self.sx = sx
        self.sy = sy
        self.color = color
        self.alive = True

    def move(self, screen):
        """move"""
        self.x + = self.sx
        self.y + = self.sy
        if self.x - self.radius <= 0 or \
                self.x + self.radius >= screen.get_width():
            self.sx = -self.sx
        if self.y - self.radius <= 0 or \
                self.y + self.radius >= screen.get_height():
            self.sy = -self.sy

    def eat(self, other):
        """Eat other balls"""
        if self.alive and other.alive and self != other:
            dx, dy = self.x - other.x, self.y - other.y
            distance = sqrt(dx ** 2 + dy ** 2)
            if distance < self.radius + other.radius \
                    and self.radius > other.radius:
                other.alive = False
                self.radius = self.radius + int(other.radius * 0.146)

    def draw(self, screen):
        """Draw the ball on the window"""
        pygame.draw.circle(screen, self.color,
                           (self.x, self.y), self.radius, 0)
Event handling

Mouse events can be processed in the event loop. The event type can be determined through the type attribute of the event object, and the position of the mouse click can be obtained through the pos attribute. If you want to handle keyboard events, this is also the place. The method is similar to handling mouse events.

def main():
    # Define the container used to hold all balls
    balls = []
    # Initialize imported modules in pygame
    pygame.init()
    # Initialize the window for display and set the window size
    screen = pygame.display.set_mode((800, 600))
    #Set the title of the current window
    pygame.display.set_caption('Big ball eats small ball')
    running=True
    # Start an event loop to process the events that occur
    while running:
        # Get events from the message queue and process them
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running=False
            #Code to handle mouse events
            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                # Get the position of the mouse click
                x, y = event.pos
                radius = randint(10, 100)
                sx, sy = randint(-10, 10), randint(-10, 10)
                color = Color.random_color()
                #Create a ball at the mouse click location (random size, speed and color)
                ball = Ball(x, y, radius, sx, sy, color)
                # Add the ball to the list container
                balls.append(ball)
        screen.fill((255, 255, 255))
        # Take out the ball in the container. If it is not eaten, draw it. If it is eaten, remove it.
        for ball in balls:
            if ball.alive:
                ball.draw(screen)
            else:
                balls.remove(ball)
        pygame.display.flip()
        # Change the position of the ball every 50 milliseconds and refresh the window
        pygame.time.delay(50)
        for ball in balls:
            ball.move(screen)
            # Check if the ball has eaten other balls
            for other in balls:
                ball.eat(other)


if __name__ == '__main__':
    main()

Putting the above two pieces of code together, we have completed the “big ball eats small ball” game (as shown in the picture below). To be precise, it is not a game, but we have passed the basic knowledge of making a small game. This example tells everyone that with this knowledge you can start your mini game development journey. In fact, there are many areas worth improving in the above code. For example, the code for refreshing the window and moving the ball should not be placed in the event loop. After learning the knowledge of multi-threading, it is possible to use a background thread to handle these things. is a better choice. If we want to get a better user experience, we can also add background music to the game and play sound effects when the ball collides with the ball. Using the mixer and music modules of pygame, we can easily do this. You can Learn about this. In fact, if you want to know more about pygame, the best tutorial is the official website of pygame. If you have no problem with English, you can go and have a look. If you want to develop 3D games, pygame seems to be insufficient. Readers who are interested in 3D game development may wish to take a look at Panda3D.