|
| 1 | +Making Apps with Pygame |
| 2 | +======================= |
| 3 | + |
| 4 | +In this tutorial we are going to create applications and games with Pygame. |
| 5 | +Pygame only allows one single window. Within one window, we can switch Scenes. |
| 6 | +Each scene has objects. |
| 7 | + |
| 8 | +The App class |
| 9 | +------------- |
| 10 | + |
| 11 | +We are going to build an app based on pygame. So the first thing to do is to import |
| 12 | +the module, as well as a series of useful constants:: |
| 13 | + |
| 14 | + import pygame |
| 15 | + from pygame.locals import * |
| 16 | + |
| 17 | +Then we create define the App class which initializes pygame and opens a the app |
| 18 | +window:: |
| 19 | + |
| 20 | + class App: |
| 21 | + """Create a single-window app with multiple scenes.""" |
| 22 | + |
| 23 | + def __init__(self): |
| 24 | + """Initialize pygame and the application.""" |
| 25 | + pygame.init() |
| 26 | + flags = RESIZABLE |
| 27 | + App.screen = pygame.display.set_mode((640, 240), flags) |
| 28 | + |
| 29 | + App.running = True |
| 30 | + |
| 31 | +Further we have to define the main event loop:: |
| 32 | + |
| 33 | + def run(self): |
| 34 | + """Run the main event loop.""" |
| 35 | + while App.running: |
| 36 | + for event in pygame.event.get(): |
| 37 | + if event.type == QUIT: |
| 38 | + App.running = False |
| 39 | + pygame.quit() |
| 40 | + |
| 41 | +At the end of the module we run a demo, if the programm is run directly and not |
| 42 | +imported as a module:: |
| 43 | + |
| 44 | + if __name__ == '__main__': |
| 45 | + App().run() |
| 46 | + |
| 47 | + |
| 48 | +Add text |
| 49 | +-------- |
| 50 | + |
| 51 | +Now we add some text to the screen. We create a Text class from which we can |
| 52 | +instantiate text objects:: |
| 53 | + |
| 54 | + class Text: |
| 55 | + """Create a text object which knows how to draw itself.""" |
| 56 | + |
| 57 | + def __init__(self, text, pos, **options): |
| 58 | + """Instantiate and render the text object.""" |
| 59 | + self.str = text |
| 60 | + self.pos = pos |
| 61 | + self.fontsize = 72 |
| 62 | + self.fontcolor = Color('black') |
| 63 | + self.render() |
| 64 | + |
| 65 | +The text needs to be rendered into a surface object, an image. This needs to be |
| 66 | +done only once, or whenever the text changes:: |
| 67 | + |
| 68 | + def render(self): |
| 69 | + """Render the string and create a surface object.""" |
| 70 | + self.font = pygame.font.Font(None, self.fontsize) |
| 71 | + self.text = self.font.render(self.str, True, self.fontcolor) |
| 72 | + self.rect = self.text.get_rect() |
| 73 | + |
| 74 | +Drawing the text means blitting it to the application screen:: |
| 75 | + |
| 76 | + def draw(self): |
| 77 | + """Draw the text surface on the screen.""" |
| 78 | + App.screen.blit(self.text, self.pos) |
| 79 | + |
| 80 | + |
| 81 | +Create scenes |
| 82 | +------------- |
| 83 | + |
| 84 | +Most applications or games have different scenes, such as an introduction screen, |
| 85 | +an intro, and different game levels. So we are going to define the Scene class:: |
| 86 | + |
| 87 | + class Scene: |
| 88 | + """Create a new scene (room, level, view).""" |
| 89 | + id = 0 |
| 90 | + bg = Color('gray') |
| 91 | + |
| 92 | +When creating a new scene, we append the scene to the applications scene list |
| 93 | +and make this scene the current scene:: |
| 94 | + |
| 95 | + def __init__(self, *args, **kwargs): |
| 96 | + # Append the new scene and make it the current scene |
| 97 | + App.scenes.append(self) |
| 98 | + App.scene = self |
| 99 | + |
| 100 | +The we set a scene id, which is kept as class attribute of the Scene class. |
| 101 | +Then we set the nodes list to the empty list and set the background color:: |
| 102 | + |
| 103 | + # Set the instance id and increment the class id |
| 104 | + self.id = Scene.id |
| 105 | + Scene.id += 1 |
| 106 | + self.nodes = [] |
| 107 | + self.bg = Scene.bg |
| 108 | + |
| 109 | +The scene object knows how to draw itself. It first fills the background with the |
| 110 | +background color, then draws each nodes and finally flips the display to update the |
| 111 | +screen:: |
| 112 | + |
| 113 | + def draw(self): |
| 114 | + """Draw all objects in the scene.""" |
| 115 | + App.screen.fill(self.bg) |
| 116 | + for node in self.nodes: |
| 117 | + node.draw() |
| 118 | + pygame.display.flip() |
| 119 | + |
| 120 | +The string representation of the scene is *Scene* followed by its ID number:: |
| 121 | + |
| 122 | + def __str__(self): |
| 123 | + return 'Scene {}'.format(self.id) |
| 124 | + |
| 125 | +.. image:: app1.* |
| 126 | + |
| 127 | + |
| 128 | +Shortcut keys |
| 129 | +------------- |
| 130 | + |
| 131 | +Key presses can be used to switch scenes, or to interact with the game, |
| 132 | +or to run commands. We add the following code inside the event loop to |
| 133 | +intercept the S key:: |
| 134 | + |
| 135 | + if event.type == KEYDOWN: |
| 136 | + if event.key == K_s: |
| 137 | + print('Key press S') |
| 138 | + |
| 139 | +The easiest way to represent shortcuts is under the form of a dictionary, |
| 140 | +where the keys are associated with command strings. We add the following |
| 141 | +code inside the App init method:: |
| 142 | + |
| 143 | + self.shortcuts = {K_ESCAPE: 'App.running=False', |
| 144 | + K_p: 'self.capture()', |
| 145 | + K_w: 'self.where()', |
| 146 | + K_s: 'self.next_scene()', |
| 147 | + } |
| 148 | + |
| 149 | +Inside the event loop we detect keydown events and call the key handler:: |
| 150 | + |
| 151 | + if event.type == KEYDOWN: |
| 152 | + self.do_shortcuts(event) |
| 153 | + |
| 154 | +The following method handles the shortcuts for simple keys or combinations of |
| 155 | +keys and modifier keys:: |
| 156 | + |
| 157 | + def do_shortcuts(self, event): |
| 158 | + """Check if the key/mod combination is part of the shortcuts |
| 159 | + dictionary and execute it. More shortcuts can be added |
| 160 | + to the ``self.shortcuts`` dictionary by the program.""" |
| 161 | + k = event.key |
| 162 | + m = event.mod |
| 163 | + |
| 164 | + if k in self.shortcuts and m == 0 : |
| 165 | + exec(self.shortcuts[k]) |
| 166 | + elif (k, m) in self.shortcuts: |
| 167 | + exec(self.shortcuts[k, m]) |
| 168 | + |
| 169 | + |
| 170 | + |
| 171 | + |
| 172 | + |
0 commit comments