Multiple Displays in Pygame

I'm making a little game and I want to make another window separately from my main one.

I have the the main game in a main window, and I want to open a new window and do a little animation when the user does something.

In my example code below, when the user presses "a" I want it to open a new window and blit to there.

Here I set up the two windows: (I know this doesnt work, its what I'm asking how to do)

SCREEN_X = 400
SCREEN_Y = 400
BSCREEN_X = 240
BSCREEN_Y = 160

BATTLE_SCENE = pygame.display.set_mode((BSCREEN_X, BSCREEN_Y))
SCREEN = pygame.display.set_mode((SCREEN_X, SCREEN_Y))

and then the program:

def run_ani ():
    #Do animation, blitting to BATTLE_SCENE
    return

def main_game():
    ending=False
    while ending==False:
        clock.tick(30)
        for event in pygame.event.get():
            if event.type == pygame.QUIT: ending=True
            if event.type == KEYDOWN: # key down or up?
                if event.key == K_ESCAPE:
                    ending=True # Time to leave
                    print("Stopped Early by user")
                elif event.key == K_a:
                    run_ani()
        #Normal screen motion, blitting to SCREEN
        if ending: pygame.quit()
    return

So far what this does is draws the main screen, then when A is pressed, it stops drawing the main screen animations, but still draws the other animations on the main screen and draws in the top left corner.

I'm pretty sure it does this because I am setting BATTLE_SCENE to be smaller than the main screen, thus when blitting to BATTLE_SCENE it blits to the area I created (240x160) in the top corner of the main screen.

However I want BATTLE_SCENE to be a seperate window, so that when I press 'a' it will pop up, do its thing, then close or at least go behind the main screen.

How to do this? Is it even possible?


Solution 1:

Do you really need multiple windows? I mean, do you really need them?

If yes, then you should probably use pyglet/cocos2d instead.

To have multiple windows in pygame, you need multiple processes (one for each window). While this is doable, it's not worth the efford. You'll need IPC to exchange data between the windows, and I guess your code will become error-prone and ugly.

Go with pyglet when you need more than one window.

The better solution is probably to divide your game into scenes. Create multiple scenes so that each one represent one stage of the game, something like MenuScene, MainScene, BattleScene, GameOverScene, OptionScene etc.

Then let each of those scenes handle input/drawing of that very part of the game.

  • MenuScene handles drawing and input etc. of the game's menu
  • MainScene handles drawing and input etc. of the running game
  • BattleScene handles drawing and input etc. of whatever you do in run_ani

In your mainloop, just pass control over to the current scene by implementing the methods draw(), handle_event(), and update().

Some example code to get the idea:

scenes = {'Main': MainScene(),
          'Battle': BattleScene()} #etc

scene = scenes['Main']

class MainScene():
  ...
  def handle_event(self, event):
    if event.type == KEYUP:
      if event.key == K_a:
        scene = scenes['Battle']
  ...

class BattleScene():
  ...
  def draw(self):
    # draw your animation

  def update(self):
    # if animation is over:
    scene = scenes['Main']

...

def main_game():
  ending=False
  While Not ending:
      clock.tick(30)
      for event in pygame.event.get():
        scene.handle_event(event)
        scene.update()
        scene.draw()

This is an easy way to cleanly seperate the game logic and allow context switching.

Solution 2:

======================================Edit=========================================

Actually it won't work. Apperantly pygame only supports one display screen, and when you initialize another, it will close the first. You will stay with two varibles, which in fact are the same surface. You can have instead the game increasing the window size and playing the battle scene on the side of it, to do this, you can call the pygame.display.set_mode() again with different values. The varible which references the display screen will still be usable, as it change its reference to the new one. After the scene is over you can decrease the window back the same way.

==================================================================================

What basically happens is you run a loop, and each iteration of it is rendering and displaying a new frame. When you call a function inside a loop, it doesn't continue to run until you finish running the function.

One way to solve this problen is just keep calling the function that updates the battle scene in the main loop. Another way is by using threading. Threading is basically running multiple scripts ("Threads") in the same time.

Luckily, python already implemented this for us with the threading module. It's too long for me to explain the module here, but you can learn it here. It might be a little complex if you haven't use threads before, but after some time it will be easier. And If you want to learn more about threading you can go here.

Specificly here, you can have two threads, one for each loop/window, and run them in the same time.

Hope I helped you!