1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# 4# C++ version Copyright (c) 2006-2007 Erin Catto http://www.box2d.org 5# Python version Copyright (c) 2010 kne / sirkne at gmail dot com 6# 7# This software is provided 'as-is', without any express or implied 8# warranty. In no event will the authors be held liable for any damages 9# arising from the use of this software. 10# Permission is granted to anyone to use this software for any purpose, 11# including commercial applications, and to alter it and redistribute it 12# freely, subject to the following restrictions: 13# 1. The origin of this software must not be misrepresented; you must not 14# claim that you wrote the original software. If you use this software 15# in a product, an acknowledgment in the product documentation would be 16# appreciated but is not required. 17# 2. Altered source versions must be plainly marked as such, and must not be 18# misrepresented as being the original software. 19# 3. This notice may not be removed or altered from any source distribution. 20 21""" 22A simple, minimal Pygame-based backend. 23It will only draw and support very basic keyboard input (ESC to quit). 24 25There are no main dependencies other than the actual test you are running. 26Note that this only relies on framework.py for the loading of this backend, 27and holding the Keys class. If you write a test that depends only on this 28backend, you can remove references to that file here and import this module 29directly in your test. 30 31To use this backend, try: 32 % python -m examples.web --backend simple 33 34NOTE: Examples with Step() re-implemented are not yet supported, as I wanted 35to do away with the Settings class. This means the following will definitely 36not work: Breakable, Liquid, Raycast, TimeOfImpact, ... (incomplete) 37""" 38 39import pygame 40from pygame.locals import (QUIT, KEYDOWN, KEYUP) 41import Box2D 42from Box2D.b2 import (world, staticBody, dynamicBody, kinematicBody, 43 polygonShape, circleShape, edgeShape, loopShape) 44 45TARGET_FPS = 60 46PPM = 10.0 47TIMESTEP = 1.0 / TARGET_FPS 48VEL_ITERS, POS_ITERS = 10, 10 49SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600 50SCREEN_OFFSETX, SCREEN_OFFSETY = SCREEN_WIDTH * 1.0 / 2.0, SCREEN_HEIGHT * 2.0 / 3 51colors = { 52 staticBody: (255, 255, 255, 255), 53 dynamicBody: (127, 127, 127, 255), 54 kinematicBody: (127, 127, 230, 255), 55} 56 57 58def fix_vertices(vertices): 59 return [(int(SCREEN_OFFSETX + v[0]), int(SCREEN_OFFSETY - v[1])) for v in vertices] 60 61 62def _draw_polygon(polygon, screen, body, fixture): 63 transform = body.transform 64 vertices = fix_vertices([transform * v * PPM for v in polygon.vertices]) 65 pygame.draw.polygon( 66 screen, [c / 2.0 for c in colors[body.type]], vertices, 0) 67 pygame.draw.polygon(screen, colors[body.type], vertices, 1) 68polygonShape.draw = _draw_polygon 69 70 71def _draw_circle(circle, screen, body, fixture): 72 position = fix_vertices([body.transform * circle.pos * PPM])[0] 73 pygame.draw.circle(screen, colors[body.type], 74 position, int(circle.radius * PPM)) 75circleShape.draw = _draw_circle 76 77 78def _draw_edge(edge, screen, body, fixture): 79 vertices = fix_vertices( 80 [body.transform * edge.vertex1 * PPM, body.transform * edge.vertex2 * PPM]) 81 pygame.draw.line(screen, colors[body.type], vertices[0], vertices[1]) 82edgeShape.draw = _draw_edge 83 84 85def _draw_loop(loop, screen, body, fixture): 86 transform = body.transform 87 vertices = fix_vertices([transform * v * PPM for v in loop.vertices]) 88 v1 = vertices[-1] 89 for v2 in vertices: 90 pygame.draw.line(screen, colors[body.type], v1, v2) 91 v1 = v2 92loopShape.draw = _draw_loop 93 94 95def draw_world(screen, world): 96 # Draw the world 97 for body in world.bodies: 98 for fixture in body.fixtures: 99 fixture.shape.draw(screen, body, fixture) 100 101 102class Keys(object): 103 pass 104 105# The following import is only needed to do the initial loading and 106# overwrite the Keys class. 107import framework 108# Set up the keys (needed as the normal framework abstracts them between 109# backends) 110keys = [s for s in dir(pygame.locals) if s.startswith('K_')] 111for key in keys: 112 value = getattr(pygame.locals, key) 113 setattr(Keys, key, value) 114framework.Keys = Keys 115 116 117class SimpleFramework(object): 118 name = 'None' 119 description = '' 120 121 def __init__(self): 122 self.world = world() 123 124 print('Initializing pygame framework...') 125 # Pygame Initialization 126 pygame.init() 127 caption = "Python Box2D Testbed - Simple backend - " + self.name 128 pygame.display.set_caption(caption) 129 130 # Screen and debug draw 131 self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) 132 self.font = pygame.font.Font(None, 15) 133 134 self.groundbody = self.world.CreateBody() 135 136 def run(self): 137 """ 138 Main loop. 139 140 Updates the world and then the screen. 141 """ 142 143 running = True 144 clock = pygame.time.Clock() 145 while running: 146 for event in pygame.event.get(): 147 if event.type == QUIT or (event.type == KEYDOWN and event.key == Keys.K_ESCAPE): 148 running = False 149 elif event.type == KEYDOWN: 150 self.Keyboard(event.key) 151 elif event.type == KEYUP: 152 self.KeyboardUp(event.key) 153 154 self.screen.fill((0, 0, 0)) 155 156 self.textLine = 15 157 158 # Step the world 159 self.world.Step(TIMESTEP, VEL_ITERS, POS_ITERS) 160 self.world.ClearForces() 161 162 draw_world(self.screen, self.world) 163 164 # Draw the name of the test running 165 self.Print(self.name, (127, 127, 255)) 166 167 if self.description: 168 # Draw the name of the test running 169 for s in self.description.split('\n'): 170 self.Print(s, (127, 255, 127)) 171 172 pygame.display.flip() 173 clock.tick(TARGET_FPS) 174 self.fps = clock.get_fps() 175 176 self.world.contactListener = None 177 self.world.destructionListener = None 178 self.world.renderer = None 179 180 def Print(self, str, color=(229, 153, 153, 255)): 181 """ 182 Draw some text at the top status lines 183 and advance to the next line. 184 """ 185 self.screen.blit(self.font.render( 186 str, True, color), (5, self.textLine)) 187 self.textLine += 15 188 189 def Keyboard(self, key): 190 """ 191 Callback indicating 'key' has been pressed down. 192 The keys are mapped after pygame's style. 193 194 from .framework import Keys 195 if key == Keys.K_z: 196 ... 197 """ 198 pass 199 200 def KeyboardUp(self, key): 201 """ 202 Callback indicating 'key' has been released. 203 See Keyboard() for key information 204 """ 205 pass 206