How does it work: Part 2: The main game loop

Most games have a main loop that keeps repeating whilst the game is being played. This page breaks down that main loop and explains what each part does:

# keep looping
while True:
    
    # model gravity: increase speed downwards until it reaches terminal velocity
    if speed_y < 5:
        speed_y += 1
    dy += speed_y
        
    # hold button a to move right
    if button_a.is_pressed():
        dx -= 5
    
    # hold butotn b to move left
    if button_b.is_pressed():
        dx += 5
    
    # make sure doodle x and y coordinates are between 0 and 99
    dy = check_bounds(dy, 0, 99)
    dx = check_bounds(dx, 0, 99)
    
    # show the platforms
    display.show(background)
    
    # scale the doodle coordinates down to fit on the micro:bit screen
    dx_scaled = int(5*dx/100)
    dy_scaled = int(5*dy/100)
    
    # increase frame counter
    frame += 1
    
    # scroll down
    if frame % levels[current_level] == 0:
        score += 1
        background = background.shift_down(1)
        
        # advance to next level
        if score % SCORE_BETWEEN_LEVELS == 0:
            current_level += 1
            display.scroll("Level " + str(current_level + 1))
            if current_level >= len(levels):
                current_level = len(levels) - 1
        
        # create new platform
        if score % GAP_BETWEEN_PLATFORMS == 0:
            platform_start = random.randint(0,3)
            platform_end = random.randint(platform_start + 1, 4)
            for x in range(platform_start, platform_end):
                background.set_pixel(x, 0, 3)
    
    # jump if landing on a platform
    if background.get_pixel(dx_scaled, dy_scaled) == 3 and speed_y > 2:
        speed_y = -11
        
    # check if fallen to the floor
    elif dy_scaled == 4:
        display.show(Image.SAD)
        sleep(500)
        display.scroll("Score: " + str(score))
        break
    
    # draw jumping doodle
    display.set_pixel(dx_scaled, dy_scaled, 9)
    
    sleep(20)

The while True  loop on line 36 makes all of the lines that are indented beneath it (lines 38 – 96) repeat until line 93 breaks out of the loop with break when the game is over:

    # model gravity: increase speed downwards until it reaches terminal velocity
    if speed_y < 5:
        speed_y += 1
    dy += speed_y

The variable speed_ystores the speed that the doodle jump alien is falling down. Lines 39-40 make the alien accelerate downwards up to a maximum speed of 5 going down. Line 41 increases the y coordinate of the alien by the value stored inspeed_y . This means that all the code has to do in order to make the alien jump is set speed_y to a negative number. When this happens on line 86, the y coordinate of the alien decreases, which moves the alien up towards the top of the screen, before gravity (or lines 39 and 40) bring it back down again.

    # hold button a to move right
    if button_a.is_pressed():
        dx -= 5
    
    # hold butotn b to move left
    if button_b.is_pressed():
        dx += 5

Lines 44 and 48 check to see if button A or button B are currently being pressed. If so , the x coordinate of the alien (dx ) is decreased or increased respectively.

Curious computing

Try changing the values and see what happens

Try changing the amount that dxis increased or decreased by on lines 45 and 49. Can you find a value that works better for the game than 5?

 

 

 

 

    # make sure doodle x and y coordinates are between 0 and 99
    dy = check_bounds(dy, 0, 99)
    dx = check_bounds(dx, 0, 99)

We’ve already talked about lines 51 -53 on the previous page. Here, we call the function check_boundsthat we defined on lines 10-15:

# force a value to be between a minimum and maximum
def check_bounds(val, min, max):
    if val < min:
        val = min
    if val > max:
        val = max
    return val

A function is a section of code that we can use more than once that calculate something that we can use or store somewhere else in the program.

The function definition (that’s what def  stands for on line 10) tells python what the function should do, but it doesn’t actually do it until the function is called. Once you’ve defined a function, you can call it as many times as you like, so it’s a great way of making your code more efficient because you can avoid repeating the same lines of code all over your program.

    # show the platforms
    display.show(background)

Line 56 displays the image background  to the LED screen on the micro:bit. This image contains numbers which represent the brightness of each LED. We use the number 3 (where 9 is full brightness and 0 and completely off) to represent a platform that the doodle jump alien can jump from.

    # scale the doodle coordinates down to fit on the micro:bit screen
    dx_scaled = int(5*dx/100)
    dy_scaled = int(5*dy/100)

The micro:bit only has an LED screen that’s 5×5 pixels in size but we’re using dimensions for dx and dy (the doodle jump alien’s coordinates) between 0 and 99 rather than 0 and 4. This is so that we can keep track of the speed and position of the alien with more precision so that it appears to move more smoothly.

Lines 59 and 60 scale dxand dydown  from between 0-99 to being between 0-4.

The intfunction is used to round down the scaled coordinates so that there are no decimal places: it converts the coordinates into integer values.

    # increase frame counter
    frame += 1
    
    # scroll down
    if frame % levels[current_level] == 0:
        score += 1
        background = background.shift_down(1)

The variable frame keeps track of how many times the main game loops round. Line 63 increments this value (makes it increase by 1).

On line 32, a list called levels is defined with the values: [30, 25, 20, 18, 15, 12, 10] . We’ve also got a variable called current_level which is set to 0 on line 33.

levels[current_level]on line 66 looks up the value in  levelsfor the current level (which will be the first number: 30 when current_levelis 0)

The whole of line 66: if frame % levels[current_level] == 0  means if the number in the levels list for the current level can be divided by the current frame number, without leaving any remainder, then run the lines that are indented underneath.

This is a good way of making lines 67-82 not run every time the main game loop repeats. These lines will start off running every 30th frame (for level 1), then they’ll run every 25 frames (for level 2), then every 20 frames, running more frequently as the game level  – and difficulty – increases.

Lines 67 and 68 are much simpler. They just increase the score and make the platforms move downwards.

I’m impressed if you’ve managed to follow all of that so far. The next page is going to give you some code with some deliberate mistakes in the rest of the main game loop so you can see if you can find and fix them for yourself.