0

I am developing a small game for learning purposes. I have created a simple animation for the title screen. Since there is also a function for full screen in the code, I wanted to create a title screen that:

  1. Displayed the animation
  2. Turned into full screen when the key was activated
  3. Continued the animation at the point it was before activating full screen

In order to do this, I resorted to threading. However, this is the first time I tried to do any multi-threading, and I don´t know what did I do wrong. The result is an undetermined error.

The code for the title screen is this:

try:
    GameAnimation = threading.Thread(target=GameTitleAnimation, (Window, WindowDimensions, FontDictionary, CurrentVersion))
    GameAnimation.start()
except:
    print "There was an error while loading the screen. Press one key to exit the program."
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            Quit()
        if event.type == pygame.KEYDOWN:
            if event.key == K_ESCAPE:
                Quit()
            elif event.key == K_f:
                Fullscreen(Window, WindowDimensions)
            else:
                return

The code for the animation is:

TitleWhite = [255, 255, 255, 0]
Black = BASE_BLACK
TitleLetters = ("R", "O", "G", "U", "E", " ", "H", "U", "N", "T", "E", "R")
Title = FontDictionary["TitleFont"][1].render("ROGUE HUNTER", False, TitleWhite)
TextWidth = Title.get_width()
TextHeight = Title.get_height()
TitleXPosition = (WindowDimensions[0] - TextWidth) / 2
TitleYPosition = (WindowDimensions[1] / 2) - (TextHeight / 2)
for letter in TitleLetters:
    if letter == " ":
       TitleXPosition += CurrentLetterWidth
    else:
        while TitleWhite[3] < 100:
            TitleWhite[3] += 1
            CurrentLetter = FontDictionary["TitleFont"][1].render(letter, False, TitleWhite)
            CurrentLetter.set_alpha(TitleWhite[3])
            Window.blit(CurrentLetter, (TitleXPosition, TitleYPosition))
            time.sleep(0.008)
            try: 
                pygame.display.update()
            except Exception:
                traceback.print_exception
        TitleWhite[3] = 0
        CurrentLetterWidth = CurrentLetter.get_width()
        TitleXPosition += CurrentLetterWidth
FadeInSurface = pygame.Surface((WindowDimensions[0], WindowDimensions[1]))
FadeInSurface.fill(TitleWhite)
OpacityRounds = 1
while TitleWhite[3] < 100.0:
    TitleWhite[3] = 1.1 ** OpacityRounds
    FadeInSurface.set_alpha(TitleWhite[3])
    Window.blit(FadeInSurface, (0, 0))
    OpacityRounds += 1
    pygame.display.update()
    time.sleep (0.015)
time.sleep(0.7)  
TitleXPosition = (WindowDimensions[0] - TextWidth) / 2
Version = FontDictionary["BodyFont"][1].render(CURRENT_VERSION, False, TitleWhite)
VersionHeight = Version.get_height()
VersionWidth = Version.get_width()
VersionXPosition = (WindowDimensions[0] - VersionWidth) / 2
VersionYPosition = TitleYPosition + TextHeight
while True:
    pygame.draw.rect(Window, Black, (0, 0, WindowDimensions[0], WindowDimensions[1]), 0)
    Window.blit(Title, (TitleXPosition, TitleYPosition))
    Window.blit(Version, (VersionXPosition, VersionYPosition))
    pygame.display.update()

I'd be very grateful if anyone could help me with this. I am going crazy.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • General comment: Your code would be much easier for everyone to read and understand if *you* would read and start following the guideline in the [PEP 8 - Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/). – martineau Nov 08 '18 at 16:23
  • What does "The result is an undetermined error" mean—something (or perhaps nothing) happens, right? What _exactly_ occurs when you try to run your code? – martineau Nov 08 '18 at 16:31
  • Please don't post only code snippets, provide a [minimal, complete (runnable) and verifiable example](https://stackoverflow.com/help/mcve) instead that we can copy, paste and test without modifications. -- Also, you usually don't have to use multithreading in pygame. If you explain your goals and problems more thoroughly, we can probably provide alternative solutions. – skrx Nov 08 '18 at 17:33

2 Answers2

1

There's no reason to use threading in your code. It will only make your code harder to read, harder to debug and error prone.

Usually you want to have some kind of state in your game that you use to determinate what should happen in a frame. You can find a class based example here.

Another way to handle this, which is a bit similar to your code, is to use coroutines.

Look at your animation code and instead of calling pygame.display.update(), give the control back to the main loop. The main loop will handle events, frame limiting and drawing, then give control back to the coroutine (which keeps track of it's own state).

Here's a simple hacky example:

import pygame
import pygame.freetype

pygame.init()
size = (640, 480)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()

def game_state(surf):
    rect = pygame.Rect(200, 200, 32, 32)
    while True:
        events = yield
        pressed = pygame.key.get_pressed()
        x = 1 if pressed[pygame.K_RIGHT] else -1 if pressed[pygame.K_LEFT] else 0
        rect.move_ip(x*5, 0)
        pygame.draw.rect(surf, pygame.Color('dodgerblue'), rect)
        yield

def title_state(surf):
    text = 'Awesome Game'
    colors = [[255, 255, 255, 20] for letter in text]
    font = pygame.freetype.SysFont(None, 22)
    font.origin = True
    while True:
        for color in colors:
            color[3] += 33
            if color[3] > 255: color[3] = 0
            x = 200
            for (letter, c) in zip(text, colors):
                bounds = font.get_rect(letter)
                font.render_to(surf, (x, 100), letter, c)
                x += bounds.width + 1

            font.render_to(surf, (180, 150), 'press [space] to start', pygame.Color('grey'))
            events = yield
            yield

def main():
    title = title_state(screen)
    game = game_state(screen)
    state = title

    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
            if e.type == pygame.KEYDOWN:
                if e.key == pygame.K_ESCAPE:
                    return
                if e.key == pygame.K_SPACE:
                    state = game if state == title else title
                if e.key == pygame.K_f:
                    if screen.get_flags() & pygame.FULLSCREEN:
                        pygame.display.set_mode(size)
                    else:
                        pygame.display.set_mode(size, pygame.FULLSCREEN)

        screen.fill(pygame.Color('grey12'))
        next(state)
        state.send(events)
        pygame.display.update()
        clock.tick(60)

if __name__ == '__main__':
    main()

See how the main loop is clean and simple, and all of the game state is handled in the coroutines. The title screen part of the code does not care about fullscreen or not or how to switch to fullscreen, and the main loop does not care of what the title screen coroutine does. And we don't need threading.

enter image description here

In practice it's not that different from the class based example I linked above, but using coroutines makes it easy to implement the title screen animation.

Basically you have an endless loop, you mutate some state (like the color of a letter), and then say "now draw this!" by just calling yield.

sloth
  • 99,095
  • 21
  • 171
  • 219
0

This is a large chunk of code to debug.

I'm not familiar with pygame or python threading, but it seems to me that you need to include some debug lines to determine exactly where the error occurs during your game animation thread (if it's even occuring there at all).

Something like this pattern should help determine the source of the problem:

import logging

logging.info("start animation initialization")
...
logging.info("begin animation loop")
...
logging.info("end animation loop")

https://docs.python.org/2/howto/logging.html

Curt Toppin
  • 19
  • 1
  • 5