├── Appendix B ├── ant.png ├── ants_game.py ├── gameobjects │ ├── __pycache__ │ │ ├── util.cpython-34.pyc │ │ └── vector2.cpython-34.pyc │ ├── util.py │ └── vector2.py ├── leaf.png ├── setup.py └── spider.png ├── Chapter 10 ├── 10-1.py ├── 10-2.py ├── 10-3.py ├── 10-4.py ├── 10-5.py ├── 10-6.py ├── ball.png ├── bar.png ├── bounce.wav ├── bouncesound.py ├── cross.png ├── gameobjects │ ├── __pycache__ │ │ ├── matrix44.cpython-34.pyc │ │ ├── util.cpython-34.pyc │ │ ├── vector2.cpython-34.pyc │ │ └── vector3.cpython-34.pyc │ ├── matrix44.py │ ├── util.py │ ├── vector2.py │ └── vector3.py ├── jukebox.py ├── mousecursor.png ├── music │ └── please put some ogg files in the music folder.ogg ├── next.png ├── pause.png ├── play.png ├── prev.png └── stop.png ├── Chapter 11 ├── 11-1.py ├── 11-2.1.py ├── 11-2.2.py ├── 11-2.3.py ├── 11-2.4.py ├── 11-2.5.py ├── 11-2.py ├── 11-3.py ├── 11-4.py ├── 11-5.py ├── 11-6.py ├── 11-7.py ├── 11-8.py ├── bodytex.jpg ├── booktex.png ├── gameobjects │ ├── __pycache__ │ │ ├── matrix44.cpython-34.pyc │ │ ├── util.cpython-34.pyc │ │ ├── vector2.cpython-34.pyc │ │ └── vector3.cpython-34.pyc │ ├── matrix44.py │ ├── util.py │ ├── vector2.py │ └── vector3.py ├── model3d.py ├── mytank.mtl ├── mytank.obj ├── mytanktex.jpg ├── opengltex.py ├── opengltextiled.py ├── sushitex.png └── tankdemo.py ├── Chapter 12 ├── 12-1.py ├── 12-2.py ├── 12-3.py ├── 12-4.py ├── 12-5.py ├── 12-6.py ├── background.png ├── blenddemo.py ├── bodytex.jpg ├── booktex.png ├── fogdemo.py ├── fugu.png ├── gameobjects │ ├── __pycache__ │ │ ├── matrix44.cpython-34.pyc │ │ ├── util.cpython-34.pyc │ │ ├── vector2.cpython-34.pyc │ │ └── vector3.cpython-34.pyc │ ├── matrix44.py │ ├── util.py │ ├── vector2.py │ └── vector3.py ├── heavyfogtank.py.py ├── model3d.py ├── mytank.mtl ├── mytank.obj ├── mytanktex.jpg ├── skybox.py └── tanksky │ ├── bk.png │ ├── dn.png │ ├── ft.png │ ├── lt.png │ ├── rt.png │ ├── skybox.mtl │ ├── skybox.obj │ └── up.png ├── Chapter 2 ├── tank.py └── tankgame.py ├── Chapter 3 ├── 3-1.py ├── 3-2.py ├── 3-3.py ├── 3-4.py ├── 3-5.py ├── 3-6.py ├── 3-7.py └── name.png ├── Chapter 4 ├── 4-1.py ├── 4-10.py ├── 4-11.py ├── 4-12.py ├── 4-13.py ├── 4-14.py ├── 4-15.py ├── 4-2.py ├── 4-3.py ├── 4-4.py ├── 4-5.py ├── 4-6.py ├── 4-7.py ├── 4-8.py ├── 4-9.py └── allcolors.bmp ├── Chapter 5 ├── 5-1.py ├── 5-10.py ├── 5-11.py ├── 5-12.py ├── 5-13.py ├── 5-14.py ├── 5-15.py ├── 5-16.py ├── 5-17.py ├── 5-2.py ├── 5-3.py ├── 5-4.py ├── 5-5.py ├── 5-6.py ├── 5-7.py ├── 5-8.py ├── 5-9.py ├── fugu.png ├── gameobjects │ └── vector2.py └── sushiplate.jpg ├── Chapter 6 ├── 6-1.py ├── 6-2.py ├── 6-3.py ├── 6-4.py ├── 6-5.py ├── 6-6.py ├── 6-7.py ├── 6-8.py ├── fugu.png ├── gameobjects │ ├── __pycache__ │ │ └── vector2.cpython-34.pyc │ └── vector2.py ├── map.png └── sushiplate.jpg ├── Chapter 7 ├── 7-1.py ├── 7-2.py ├── 7-3.py ├── 7-4.py ├── 7-5.py ├── 7-6.py ├── 7-7.py ├── 7-8.py ├── 7-9.py ├── ant.png ├── gameobjects │ ├── util.py │ ├── vector2-init.py │ └── vector2.py ├── leaf.png └── spider.png ├── Chapter 8 ├── 8-1.py ├── 8-2.py ├── 8-3.py ├── 8-4.py ├── 8-5.py ├── 8-6.py ├── 8-7.py ├── 8-8.py ├── ball.png └── gameobjects │ ├── __pycache__ │ ├── util.cpython-34.pyc │ ├── vector2.cpython-34.pyc │ └── vector3.cpython-34.pyc │ ├── util.py │ ├── vector2.py │ └── vector3.py ├── Chapter 9 ├── 9-1.py ├── 9-2.py ├── 9-3.py ├── 9-4.py ├── 9-5.py ├── 9-6.py ├── 9-7.py ├── 9-8.py ├── ball.png ├── firstopengl.py ├── gameobjects │ ├── __pycache__ │ │ ├── matrix44.cpython-34.pyc │ │ ├── util.cpython-34.pyc │ │ ├── vector2.cpython-34.pyc │ │ └── vector3.cpython-34.pyc │ ├── matrix44.py │ ├── util.py │ ├── vector2.py │ └── vector3.py └── map.png ├── LICENSE ├── README.md └── gameobjects ├── __init__.py ├── color.py ├── gametime.py ├── grid.py ├── locals.py ├── matrix44.py ├── sphere.py ├── test_vector3.py ├── util.py ├── vector2.py └── vector3.py /Appendix B/ant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Appendix B/ant.png -------------------------------------------------------------------------------- /Appendix B/gameobjects/__pycache__/util.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Appendix B/gameobjects/__pycache__/util.cpython-34.pyc -------------------------------------------------------------------------------- /Appendix B/gameobjects/__pycache__/vector2.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Appendix B/gameobjects/__pycache__/vector2.cpython-34.pyc -------------------------------------------------------------------------------- /Appendix B/gameobjects/util.py: -------------------------------------------------------------------------------- 1 | from math import * 2 | 3 | def format_number(n, accuracy=6): 4 | """Formats a number in a friendly manner 5 | (removes trailing zeros and unneccesary point.""" 6 | 7 | fs = "%."+str(accuracy)+"f" 8 | str_n = fs%float(n) 9 | if '.' in str_n: 10 | str_n = str_n.rstrip('0').rstrip('.') 11 | if str_n == "-0": 12 | str_n = "0" 13 | #str_n = str_n.replace("-0", "0") 14 | return str_n 15 | 16 | 17 | def lerp(a, b, i): 18 | """Linear enterpolate from a to b.""" 19 | return a+(b-a)*i 20 | 21 | 22 | def range2d(range_x, range_y): 23 | 24 | """Creates a 2D range.""" 25 | 26 | range_x = list(range_x) 27 | return [ (x, y) for y in range_y for x in range_x ] 28 | 29 | 30 | def xrange2d(range_x, range_y): 31 | 32 | """Iterates over a 2D range.""" 33 | 34 | range_x = list(range_x) 35 | for y in range_y: 36 | for x in range_x: 37 | yield (x, y) 38 | 39 | 40 | def saturate(value, low, high): 41 | return min(max(value, low), high) 42 | 43 | 44 | def is_power_of_2(n): 45 | """Returns True if a value is a power of 2.""" 46 | return log(n, 2) % 1.0 == 0.0 47 | 48 | 49 | def next_power_of_2(n): 50 | """Returns the next power of 2 that is >= n""" 51 | return int(2 ** ceil(log(n, 2))) 52 | 53 | if __name__ == "__main__": 54 | 55 | print(list( xrange2d(xrange(3), xrange(3)) )) 56 | print(range2d(xrange(3), xrange(3))) 57 | print(is_power_of_2(7)) 58 | print(is_power_of_2(8)) 59 | print(is_power_of_2(9)) 60 | 61 | print(next_power_of_2(7)) 62 | -------------------------------------------------------------------------------- /Appendix B/leaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Appendix B/leaf.png -------------------------------------------------------------------------------- /Appendix B/setup.py: -------------------------------------------------------------------------------- 1 | import cx_Freeze 2 | 3 | executables = [cx_Freeze.Executable("ants_game.py")] 4 | 5 | cx_Freeze.setup( 6 | name="Ant Game", 7 | options={"build_exe": {"packages":["pygame"], 8 | "include_files":["ant.png","leaf.png","spider.png",'gameobjects']}}, 9 | executables = executables 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /Appendix B/spider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Appendix B/spider.png -------------------------------------------------------------------------------- /Chapter 10/10-1.py: -------------------------------------------------------------------------------- 1 | def stereo_pan(x_coord, screen_width): 2 | 3 | right_volume = float(x_coord) / screen_width 4 | left_volume = 1.0 - right_volume 5 | 6 | return (left_volume, right_volume) 7 | -------------------------------------------------------------------------------- /Chapter 10/10-2.py: -------------------------------------------------------------------------------- 1 | tank.explode() # Do explosion visual 2 | explosion_channel = explosion_sound.play() 3 | if explosion_channel is not None: 4 | left, right = stereo_pan(tank.position.x, SCREEN_SIZE[0]) 5 | explosion_channel.set_volume(left, right) 6 | -------------------------------------------------------------------------------- /Chapter 10/10-3.py: -------------------------------------------------------------------------------- 1 | pygame.mixer.set_reserved(2) 2 | reserved_channel_0 = pygame.mixer.Channel(0) 3 | reserved_channel_1 = pygame.mixer.Channel(1) 4 | -------------------------------------------------------------------------------- /Chapter 10/10-4.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from random import randint 4 | from gameobjects.vector2 import Vector2 5 | 6 | SCREEN_SIZE = (640, 480) 7 | 8 | # In pixels per second, per second 9 | GRAVITY = 250.0 10 | # Increase for more bounciness, but don't go over 1! 11 | BOUNCINESS = 0.7 12 | 13 | def stero_pan(x_coord, screen_width): 14 | 15 | right_volume = float(x_coord) / screen_width 16 | left_volume = 1.0 - right_volume 17 | 18 | return (left_volume, right_volume) 19 | 20 | 21 | class Ball(object): 22 | 23 | def __init__(self, position, speed, image, bounce_sound): 24 | 25 | self.position = Vector2(position) 26 | self.speed = Vector2(speed) 27 | self.image = image 28 | self.bounce_sound = bounce_sound 29 | self.age = 0.0 30 | 31 | def update(self, time_passed): 32 | 33 | w, h = self.image.get_size() 34 | 35 | screen_width, screen_height = SCREEN_SIZE 36 | 37 | x, y = self.position 38 | x -= w/2 39 | y -= h/2 40 | 41 | # Has the ball bounce 42 | bounce = False 43 | 44 | # Has the ball hit the bottom of the screen? 45 | if y + h >= screen_height: 46 | self.speed.y = -self.speed.y * BOUNCINESS 47 | self.position.y = screen_height - h / 2.0 - 1.0 48 | bounce = True 49 | 50 | # Has the ball hit the left of the screen? 51 | if x <= 0: 52 | self.speed.x = -self.speed.x * BOUNCINESS 53 | self.position.x = w / 2.0 + 1 54 | bounce = True 55 | 56 | # Has the ball hit the right of the screen 57 | elif x + w >= screen_width: 58 | self.speed.x = -self.speed.x * BOUNCINESS 59 | self.position.x = screen_width - w / 2.0 - 1 60 | bounce = True 61 | 62 | # Do time based movement 63 | self.position += self.speed * time_passed 64 | # Add gravity 65 | self.speed.y += time_passed * GRAVITY 66 | 67 | if bounce: 68 | self.play_bounce_sound() 69 | 70 | self.age += time_passed 71 | 72 | 73 | def play_bounce_sound(self): 74 | 75 | channel = self.bounce_sound.play() 76 | 77 | if channel is not None: 78 | # Get the left and right volumes 79 | left, right = stero_pan(self.position.x, SCREEN_SIZE[0]) 80 | channel.set_volume(left, right) 81 | 82 | 83 | def render(self, surface): 84 | 85 | # Draw the sprite center at self.position 86 | w, h = self.image.get_size() 87 | x, y = self.position 88 | x -= w/2 89 | y -= h/2 90 | surface.blit(self.image, (x, y)) 91 | 92 | 93 | def run(): 94 | 95 | # Initialise 44KHz 16-bit stero sound 96 | pygame.mixer.pre_init(44100, 16, 2, 1024*4) 97 | pygame.init() 98 | pygame.mixer.set_num_channels(8) 99 | screen = pygame.display.set_mode(SCREEN_SIZE, 0) 100 | 101 | print(pygame.display.get_wm_info()) 102 | hwnd = pygame.display.get_wm_info()["window"] 103 | x, y = (200, 200) 104 | 105 | pygame.mouse.set_visible(False) 106 | 107 | clock = pygame.time.Clock() 108 | 109 | ball_image = pygame.image.load("ball.png").convert_alpha() 110 | mouse_image = pygame.image.load("mousecursor.png").convert_alpha() 111 | 112 | # Load the sound file 113 | bounce_sound = pygame.mixer.Sound("bounce.wav") 114 | 115 | balls = [] 116 | 117 | while True: 118 | 119 | for event in pygame.event.get(): 120 | 121 | if event.type == QUIT: 122 | pygame.quit() 123 | quit() 124 | 125 | if event.type == MOUSEBUTTONDOWN: 126 | 127 | # Create a new ball at the mouse position 128 | random_speed = ( randint(-400, 400), randint(-300, 0) ) 129 | new_ball = Ball( event.pos, 130 | random_speed, 131 | ball_image, 132 | bounce_sound ) 133 | balls.append(new_ball) 134 | 135 | time_passed_seconds = clock.tick() / 1000. 136 | 137 | screen.fill((255, 255, 255)) 138 | 139 | dead_balls = [] 140 | 141 | for ball in balls: 142 | 143 | ball.update(time_passed_seconds) 144 | ball.render(screen) 145 | 146 | # Make not of any balls that are older than 10 seconds 147 | if ball.age > 10.0: 148 | dead_balls.append(ball) 149 | 150 | # remove any 'dead' balls from the main list 151 | for ball in dead_balls: 152 | 153 | balls.remove(ball) 154 | 155 | # Draw the mouse cursor 156 | mouse_pos = pygame.mouse.get_pos() 157 | screen.blit(mouse_image, mouse_pos) 158 | 159 | pygame.display.update() 160 | 161 | if __name__ == "__main__": 162 | 163 | run() 164 | -------------------------------------------------------------------------------- /Chapter 10/10-5.py: -------------------------------------------------------------------------------- 1 | pygame.mixer.music.load("techno.ogg") 2 | pygame.mixer.music.play() 3 | -------------------------------------------------------------------------------- /Chapter 10/ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/ball.png -------------------------------------------------------------------------------- /Chapter 10/bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/bar.png -------------------------------------------------------------------------------- /Chapter 10/bounce.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/bounce.wav -------------------------------------------------------------------------------- /Chapter 10/bouncesound.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from random import randint 4 | from gameobjects.vector2 import Vector2 5 | 6 | SCREEN_SIZE = (640, 480) 7 | 8 | # In pixels per second, per second 9 | GRAVITY = 250.0 10 | # Increase for more bounciness, but don't go over 1! 11 | BOUNCINESS = 0.7 12 | 13 | def stero_pan(x_coord, screen_width): 14 | 15 | right_volume = float(x_coord) / screen_width 16 | left_volume = 1.0 - right_volume 17 | 18 | return (left_volume, right_volume) 19 | 20 | 21 | class Ball(object): 22 | 23 | def __init__(self, position, speed, image, bounce_sound): 24 | 25 | self.position = Vector2(position) 26 | self.speed = Vector2(speed) 27 | self.image = image 28 | self.bounce_sound = bounce_sound 29 | self.age = 0.0 30 | 31 | def update(self, time_passed): 32 | 33 | w, h = self.image.get_size() 34 | 35 | screen_width, screen_height = SCREEN_SIZE 36 | 37 | x, y = self.position 38 | x -= w/2 39 | y -= h/2 40 | 41 | # Has the ball bounce 42 | bounce = False 43 | 44 | # Has the ball hit the bottom of the screen? 45 | if y + h >= screen_height: 46 | self.speed.y = -self.speed.y * BOUNCINESS 47 | self.position.y = screen_height - h / 2.0 - 1.0 48 | bounce = True 49 | 50 | # Has the ball hit the left of the screen? 51 | if x <= 0: 52 | self.speed.x = -self.speed.x * BOUNCINESS 53 | self.position.x = w / 2.0 + 1 54 | bounce = True 55 | 56 | # Has the ball hit the right of the screen 57 | elif x + w >= screen_width: 58 | self.speed.x = -self.speed.x * BOUNCINESS 59 | self.position.x = screen_width - w / 2.0 - 1 60 | bounce = True 61 | 62 | # Do time based movement 63 | self.position += self.speed * time_passed 64 | # Add gravity 65 | self.speed.y += time_passed * GRAVITY 66 | 67 | if bounce: 68 | self.play_bounce_sound() 69 | 70 | self.age += time_passed 71 | 72 | 73 | def play_bounce_sound(self): 74 | 75 | channel = self.bounce_sound.play() 76 | 77 | if channel is not None: 78 | # Get the left and right volumes 79 | left, right = stero_pan(self.position.x, SCREEN_SIZE[0]) 80 | channel.set_volume(left, right) 81 | 82 | 83 | def render(self, surface): 84 | 85 | # Draw the sprite center at self.position 86 | w, h = self.image.get_size() 87 | x, y = self.position 88 | x -= w/2 89 | y -= h/2 90 | surface.blit(self.image, (x, y)) 91 | 92 | 93 | def run(): 94 | 95 | # Initialise 44KHz 16-bit stero sound 96 | pygame.mixer.pre_init(44100, 16, 2, 1024*4) 97 | pygame.init() 98 | pygame.mixer.set_num_channels(8) 99 | screen = pygame.display.set_mode(SCREEN_SIZE, 0) 100 | 101 | print(pygame.display.get_wm_info()) 102 | hwnd = pygame.display.get_wm_info()["window"] 103 | x, y = (200, 200) 104 | 105 | pygame.mouse.set_visible(False) 106 | 107 | clock = pygame.time.Clock() 108 | 109 | ball_image = pygame.image.load("ball.png").convert_alpha() 110 | mouse_image = pygame.image.load("mousecursor.png").convert_alpha() 111 | 112 | # Load the sound file 113 | bounce_sound = pygame.mixer.Sound("bounce.wav") 114 | 115 | balls = [] 116 | 117 | while True: 118 | 119 | for event in pygame.event.get(): 120 | 121 | if event.type == QUIT: 122 | pygame.quit() 123 | quit() 124 | 125 | if event.type == MOUSEBUTTONDOWN: 126 | 127 | # Create a new ball at the mouse position 128 | random_speed = ( randint(-400, 400), randint(-300, 0) ) 129 | new_ball = Ball( event.pos, 130 | random_speed, 131 | ball_image, 132 | bounce_sound ) 133 | balls.append(new_ball) 134 | 135 | time_passed_seconds = clock.tick() / 1000. 136 | 137 | screen.fill((255, 255, 255)) 138 | 139 | dead_balls = [] 140 | 141 | for ball in balls: 142 | 143 | ball.update(time_passed_seconds) 144 | ball.render(screen) 145 | 146 | # Make not of any balls that are older than 10 seconds 147 | if ball.age > 10.0: 148 | dead_balls.append(ball) 149 | 150 | # remove any 'dead' balls from the main list 151 | for ball in dead_balls: 152 | 153 | balls.remove(ball) 154 | 155 | # Draw the mouse cursor 156 | mouse_pos = pygame.mouse.get_pos() 157 | screen.blit(mouse_image, mouse_pos) 158 | 159 | pygame.display.update() 160 | 161 | if __name__ == "__main__": 162 | 163 | run() 164 | -------------------------------------------------------------------------------- /Chapter 10/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/cross.png -------------------------------------------------------------------------------- /Chapter 10/gameobjects/__pycache__/matrix44.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/gameobjects/__pycache__/matrix44.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 10/gameobjects/__pycache__/util.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/gameobjects/__pycache__/util.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 10/gameobjects/__pycache__/vector2.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/gameobjects/__pycache__/vector2.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 10/gameobjects/__pycache__/vector3.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/gameobjects/__pycache__/vector3.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 10/gameobjects/util.py: -------------------------------------------------------------------------------- 1 | from math import * 2 | 3 | def format_number(n, accuracy=6): 4 | """Formats a number in a friendly manner 5 | (removes trailing zeros and unneccesary point.""" 6 | 7 | fs = "%."+str(accuracy)+"f" 8 | str_n = fs%float(n) 9 | if '.' in str_n: 10 | str_n = str_n.rstrip('0').rstrip('.') 11 | if str_n == "-0": 12 | str_n = "0" 13 | #str_n = str_n.replace("-0", "0") 14 | return str_n 15 | 16 | 17 | def lerp(a, b, i): 18 | """Linear enterpolate from a to b.""" 19 | return a+(b-a)*i 20 | 21 | 22 | def range2d(range_x, range_y): 23 | 24 | """Creates a 2D range.""" 25 | 26 | range_x = list(range_x) 27 | return [ (x, y) for y in range_y for x in range_x ] 28 | 29 | 30 | def xrange2d(range_x, range_y): 31 | 32 | """Iterates over a 2D range.""" 33 | 34 | range_x = list(range_x) 35 | for y in range_y: 36 | for x in range_x: 37 | yield (x, y) 38 | 39 | 40 | def saturate(value, low, high): 41 | return min(max(value, low), high) 42 | 43 | 44 | def is_power_of_2(n): 45 | """Returns True if a value is a power of 2.""" 46 | return log(n, 2) % 1.0 == 0.0 47 | 48 | 49 | def next_power_of_2(n): 50 | """Returns the next power of 2 that is >= n""" 51 | return int(2 ** ceil(log(n, 2))) 52 | 53 | if __name__ == "__main__": 54 | 55 | print(list( xrange2d(range(3), range(3)) )) 56 | print(range2d(range(3), range(3))) 57 | print(is_power_of_2(7)) 58 | print(is_power_of_2(8)) 59 | print(is_power_of_2(9)) 60 | 61 | print(next_power_of_2(7)) 62 | -------------------------------------------------------------------------------- /Chapter 10/mousecursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/mousecursor.png -------------------------------------------------------------------------------- /Chapter 10/music/please put some ogg files in the music folder.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/music/please put some ogg files in the music folder.ogg -------------------------------------------------------------------------------- /Chapter 10/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/next.png -------------------------------------------------------------------------------- /Chapter 10/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/pause.png -------------------------------------------------------------------------------- /Chapter 10/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/play.png -------------------------------------------------------------------------------- /Chapter 10/prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/prev.png -------------------------------------------------------------------------------- /Chapter 10/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 10/stop.png -------------------------------------------------------------------------------- /Chapter 11/11-1.py: -------------------------------------------------------------------------------- 1 | from math import log, ceil 2 | def next_power_of_2(size): 3 | return 2 ** ceil(log(size, 2)) 4 | -------------------------------------------------------------------------------- /Chapter 11/11-2.1.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | SCREEN_SIZE = (800, 600) 10 | 11 | def resize(width, height): 12 | 13 | glViewport(0, 0, width, height) 14 | glMatrixMode(GL_PROJECTION) 15 | glLoadIdentity() 16 | gluPerspective(60.0, float(width)/height, .1, 1000.) 17 | glMatrixMode(GL_MODELVIEW) 18 | glLoadIdentity() 19 | 20 | def init(): 21 | 22 | glEnable(GL_TEXTURE_2D) 23 | glClearColor(1.0, 1.0, 1.0, 0.0) 24 | 25 | def run(): 26 | 27 | pygame.init() 28 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 29 | 30 | resize(*SCREEN_SIZE) 31 | init() 32 | 33 | # Load the textures 34 | texture_surface = pygame.image.load("sushitex.png") 35 | # Retrieve the texture data 36 | texture_data = pygame.image.tostring(texture_surface, 'RGB', True) 37 | 38 | # Generate a texture id 39 | texture_id = glGenTextures(1) 40 | # Tell OpenGL we will be using this texture id for texture operations 41 | glBindTexture(GL_TEXTURE_2D, texture_id) 42 | 43 | # Tell OpenGL how to scale images 44 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) 45 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ) 46 | 47 | # Tell OpenGL that data is aligned to byte boundries 48 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1) 49 | 50 | # Get the dimensions of the image 51 | width, height = texture_surface.get_rect().size 52 | 53 | 54 | gluBuild2DMipmaps( GL_TEXTURE_2D, 55 | 3, 56 | width, 57 | height, 58 | GL_RGB, 59 | GL_UNSIGNED_BYTE, 60 | texture_data ) 61 | 62 | 63 | clock = pygame.time.Clock() 64 | 65 | tex_rotation = 0.0 66 | 67 | while True: 68 | 69 | for event in pygame.event.get(): 70 | if event.type == QUIT: 71 | pygame.quit() 72 | quit() 73 | 74 | 75 | time_passed = clock.tick() 76 | time_passed_seconds = time_passed / 1000. 77 | tex_rotation += time_passed_seconds * 360.0 / 8.0 78 | 79 | 80 | # Clear the screen (similar to fill) 81 | glClear(GL_COLOR_BUFFER_BIT) 82 | 83 | # Clear the model-view matrix 84 | glLoadIdentity() 85 | 86 | # Set the modelview matrix 87 | glTranslatef(0.0, 0.0, -600.0) 88 | glRotate(tex_rotation, 1, 0, 0) 89 | 90 | # Draw a quad (4 vertices, 4 texture coords) 91 | glBegin(GL_QUADS) 92 | 93 | glTexCoord2f(0, 1) 94 | glVertex3f(-300, 300, 0) 95 | 96 | glTexCoord2f(1, 1) 97 | glVertex3f(300, 300, 0) 98 | 99 | glTexCoord2f(1, 0) 100 | glVertex3f(300, -300, 0) 101 | 102 | glTexCoord2f(0, 0) 103 | glVertex3f(-300, -300, 0) 104 | 105 | glEnd() 106 | 107 | pygame.display.flip() 108 | 109 | glDeleteTextures(texture_id) 110 | 111 | if __name__ == "__main__": 112 | run() 113 | -------------------------------------------------------------------------------- /Chapter 11/11-2.2.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | SCREEN_SIZE = (800, 600) 10 | 11 | def resize(width, height): 12 | 13 | glViewport(0, 0, width, height) 14 | glMatrixMode(GL_PROJECTION) 15 | glLoadIdentity() 16 | gluPerspective(60.0, float(width)/height, .1, 1000.) 17 | glMatrixMode(GL_MODELVIEW) 18 | glLoadIdentity() 19 | 20 | def init(): 21 | 22 | glEnable(GL_TEXTURE_2D) 23 | glClearColor(1.0, 1.0, 1.0, 0.0) 24 | 25 | def run(): 26 | 27 | pygame.init() 28 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 29 | 30 | resize(*SCREEN_SIZE) 31 | init() 32 | 33 | # Load the textures 34 | texture_surface = pygame.image.load("sushitex.png") 35 | # Retrieve the texture data 36 | texture_data = pygame.image.tostring(texture_surface, 'RGB', True) 37 | 38 | # Generate a texture id 39 | texture_id = glGenTextures(1) 40 | # Tell OpenGL we will be using this texture id for texture operations 41 | glBindTexture(GL_TEXTURE_2D, texture_id) 42 | 43 | # Tell OpenGL how to scale images 44 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) 45 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR) 46 | 47 | # Tell OpenGL that data is aligned to byte boundries 48 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1) 49 | 50 | # Get the dimensions of the image 51 | width, height = texture_surface.get_rect().size 52 | 53 | 54 | gluBuild2DMipmaps( GL_TEXTURE_2D, 55 | 3, 56 | width, 57 | height, 58 | GL_RGB, 59 | GL_UNSIGNED_BYTE, 60 | texture_data ) 61 | 62 | 63 | clock = pygame.time.Clock() 64 | 65 | tex_rotation = 0.0 66 | 67 | while True: 68 | 69 | for event in pygame.event.get(): 70 | if event.type == QUIT: 71 | pygame.quit() 72 | quit() 73 | 74 | 75 | time_passed = clock.tick() 76 | time_passed_seconds = time_passed / 1000. 77 | tex_rotation += time_passed_seconds * 360.0 / 8.0 78 | 79 | 80 | # Clear the screen (similar to fill) 81 | glClear(GL_COLOR_BUFFER_BIT) 82 | 83 | # Clear the model-view matrix 84 | glLoadIdentity() 85 | 86 | # Set the modelview matrix 87 | glTranslatef(0.0, 0.0, -600.0) 88 | glRotate(tex_rotation, 1, 0, 0) 89 | 90 | # Draw a quad (4 vertices, 4 texture coords) 91 | glBegin(GL_QUADS) 92 | 93 | glTexCoord2f(0, 1) 94 | glVertex3f(-300, 300, 0) 95 | 96 | glTexCoord2f(1, 1) 97 | glVertex3f(300, 300, 0) 98 | 99 | glTexCoord2f(1, 0) 100 | glVertex3f(300, -300, 0) 101 | 102 | glTexCoord2f(0, 0) 103 | glVertex3f(-300, -300, 0) 104 | 105 | glEnd() 106 | 107 | pygame.display.flip() 108 | 109 | glDeleteTextures(texture_id) 110 | 111 | if __name__ == "__main__": 112 | run() 113 | -------------------------------------------------------------------------------- /Chapter 11/11-2.3.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | SCREEN_SIZE = (800, 600) 10 | 11 | def resize(width, height): 12 | 13 | glViewport(0, 0, width, height) 14 | glMatrixMode(GL_PROJECTION) 15 | glLoadIdentity() 16 | gluPerspective(60.0, float(width)/height, .1, 1000.) 17 | glMatrixMode(GL_MODELVIEW) 18 | glLoadIdentity() 19 | 20 | def init(): 21 | 22 | glEnable(GL_TEXTURE_2D) 23 | glClearColor(1.0, 1.0, 1.0, 0.0) 24 | 25 | def run(): 26 | 27 | pygame.init() 28 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 29 | 30 | resize(*SCREEN_SIZE) 31 | init() 32 | 33 | # Load the textures 34 | texture_surface = pygame.image.load("sushitex.png") 35 | # Retrieve the texture data 36 | texture_data = pygame.image.tostring(texture_surface, 'RGB', True) 37 | 38 | # Generate a texture id 39 | texture_id = glGenTextures(1) 40 | # Tell OpenGL we will be using this texture id for texture operations 41 | glBindTexture(GL_TEXTURE_2D, texture_id) 42 | 43 | # Tell OpenGL how to scale images 44 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) 45 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR) 46 | 47 | # Tell OpenGL that data is aligned to byte boundries 48 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1) 49 | 50 | # Get the dimensions of the image 51 | width, height = texture_surface.get_rect().size 52 | 53 | 54 | gluBuild2DMipmaps( GL_TEXTURE_2D, 55 | 3, 56 | width, 57 | height, 58 | GL_RGB, 59 | GL_UNSIGNED_BYTE, 60 | texture_data ) 61 | 62 | 63 | clock = pygame.time.Clock() 64 | 65 | tex_rotation = 0.0 66 | 67 | while True: 68 | 69 | for event in pygame.event.get(): 70 | if event.type == QUIT: 71 | pygame.quit() 72 | quit() 73 | 74 | 75 | time_passed = clock.tick() 76 | time_passed_seconds = time_passed / 1000. 77 | tex_rotation += time_passed_seconds * 360.0 / 8.0 78 | 79 | 80 | # Clear the screen (similar to fill) 81 | glClear(GL_COLOR_BUFFER_BIT) 82 | 83 | # Clear the model-view matrix 84 | glLoadIdentity() 85 | 86 | # Set the modelview matrix 87 | glTranslatef(0.0, 0.0, -600.0) 88 | glRotate(tex_rotation, 1, 0, 0) 89 | 90 | # Draw a quad (4 vertices, 4 texture coords) 91 | glBegin(GL_QUADS) 92 | 93 | glTexCoord2f(0, 3) 94 | glVertex3f(-300, 300, 0) 95 | 96 | glTexCoord2f(3, 3) 97 | glVertex3f(300, 300, 0) 98 | 99 | glTexCoord2f(3, 0) 100 | glVertex3f(300, -300, 0) 101 | 102 | glTexCoord2f(0, 0) 103 | glVertex3f(-300, -300, 0) 104 | 105 | glEnd() 106 | 107 | pygame.display.flip() 108 | 109 | glDeleteTextures(texture_id) 110 | 111 | if __name__ == "__main__": 112 | run() 113 | -------------------------------------------------------------------------------- /Chapter 11/11-2.4.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | SCREEN_SIZE = (800, 600) 10 | 11 | def resize(width, height): 12 | 13 | glViewport(0, 0, width, height) 14 | glMatrixMode(GL_PROJECTION) 15 | glLoadIdentity() 16 | gluPerspective(60.0, float(width)/height, .1, 1000.) 17 | glMatrixMode(GL_MODELVIEW) 18 | glLoadIdentity() 19 | 20 | def init(): 21 | 22 | glEnable(GL_TEXTURE_2D) 23 | glClearColor(1.0, 1.0, 1.0, 0.0) 24 | 25 | def run(): 26 | 27 | pygame.init() 28 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 29 | 30 | resize(*SCREEN_SIZE) 31 | init() 32 | 33 | # Load the textures 34 | texture_surface = pygame.image.load("sushitex.png") 35 | # Retrieve the texture data 36 | texture_data = pygame.image.tostring(texture_surface, 'RGB', True) 37 | 38 | # Generate a texture id 39 | texture_id = glGenTextures(1) 40 | # Tell OpenGL we will be using this texture id for texture operations 41 | glBindTexture(GL_TEXTURE_2D, texture_id) 42 | 43 | # Tell OpenGL how to scale images 44 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT) 45 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT) 46 | 47 | # Tell OpenGL that data is aligned to byte boundries 48 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1) 49 | 50 | # Get the dimensions of the image 51 | width, height = texture_surface.get_rect().size 52 | 53 | 54 | gluBuild2DMipmaps( GL_TEXTURE_2D, 55 | 3, 56 | width, 57 | height, 58 | GL_RGB, 59 | GL_UNSIGNED_BYTE, 60 | texture_data ) 61 | 62 | 63 | clock = pygame.time.Clock() 64 | 65 | tex_rotation = 0.0 66 | 67 | while True: 68 | 69 | for event in pygame.event.get(): 70 | if event.type == QUIT: 71 | pygame.quit() 72 | quit() 73 | 74 | 75 | time_passed = clock.tick() 76 | time_passed_seconds = time_passed / 1000. 77 | tex_rotation += time_passed_seconds * 360.0 / 8.0 78 | 79 | 80 | # Clear the screen (similar to fill) 81 | glClear(GL_COLOR_BUFFER_BIT) 82 | 83 | # Clear the model-view matrix 84 | glLoadIdentity() 85 | 86 | # Set the modelview matrix 87 | glTranslatef(0.0, 0.0, -600.0) 88 | glRotate(tex_rotation, 1, 0, 0) 89 | 90 | # Draw a quad (4 vertices, 4 texture coords) 91 | glBegin(GL_QUADS) 92 | 93 | glTexCoord2f(0, 3) 94 | glVertex3f(-300, 300, 0) 95 | 96 | glTexCoord2f(3, 3) 97 | glVertex3f(300, 300, 0) 98 | 99 | glTexCoord2f(3, 0) 100 | glVertex3f(300, -300, 0) 101 | 102 | glTexCoord2f(0, 0) 103 | glVertex3f(-300, -300, 0) 104 | 105 | glEnd() 106 | 107 | pygame.display.flip() 108 | 109 | glDeleteTextures(texture_id) 110 | 111 | if __name__ == "__main__": 112 | run() 113 | -------------------------------------------------------------------------------- /Chapter 11/11-2.5.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | SCREEN_SIZE = (800, 600) 10 | 11 | def resize(width, height): 12 | 13 | glViewport(0, 0, width, height) 14 | glMatrixMode(GL_PROJECTION) 15 | glLoadIdentity() 16 | gluPerspective(60.0, float(width)/height, .1, 1000.) 17 | glMatrixMode(GL_MODELVIEW) 18 | glLoadIdentity() 19 | 20 | def init(): 21 | 22 | glEnable(GL_TEXTURE_2D) 23 | glClearColor(1.0, 1.0, 1.0, 0.0) 24 | 25 | def run(): 26 | 27 | pygame.init() 28 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 29 | 30 | resize(*SCREEN_SIZE) 31 | init() 32 | 33 | # Load the textures 34 | texture_surface = pygame.image.load("sushitex.png") 35 | # Retrieve the texture data 36 | texture_data = pygame.image.tostring(texture_surface, 'RGB', True) 37 | 38 | # Generate a texture id 39 | texture_id = glGenTextures(1) 40 | # Tell OpenGL we will be using this texture id for texture operations 41 | glBindTexture(GL_TEXTURE_2D, texture_id) 42 | 43 | # Tell OpenGL how to scale images 44 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 45 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 46 | 47 | # Tell OpenGL that data is aligned to byte boundries 48 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1) 49 | 50 | # Get the dimensions of the image 51 | width, height = texture_surface.get_rect().size 52 | 53 | 54 | gluBuild2DMipmaps( GL_TEXTURE_2D, 55 | 3, 56 | width, 57 | height, 58 | GL_RGB, 59 | GL_UNSIGNED_BYTE, 60 | texture_data ) 61 | 62 | 63 | clock = pygame.time.Clock() 64 | 65 | tex_rotation = 0.0 66 | 67 | while True: 68 | 69 | for event in pygame.event.get(): 70 | if event.type == QUIT: 71 | pygame.quit() 72 | quit() 73 | 74 | 75 | time_passed = clock.tick() 76 | time_passed_seconds = time_passed / 1000. 77 | tex_rotation += time_passed_seconds * 360.0 / 8.0 78 | 79 | 80 | # Clear the screen (similar to fill) 81 | glClear(GL_COLOR_BUFFER_BIT) 82 | 83 | # Clear the model-view matrix 84 | glLoadIdentity() 85 | 86 | # Set the modelview matrix 87 | glTranslatef(0.0, 0.0, -600.0) 88 | glRotate(tex_rotation, 1, 0, 0) 89 | 90 | # Draw a quad (4 vertices, 4 texture coords) 91 | glBegin(GL_QUADS) 92 | 93 | glTexCoord2f(0, 3) 94 | glVertex3f(-300, 300, 0) 95 | 96 | glTexCoord2f(3, 3) 97 | glVertex3f(300, 300, 0) 98 | 99 | glTexCoord2f(3, 0) 100 | glVertex3f(300, -300, 0) 101 | 102 | glTexCoord2f(0, 0) 103 | glVertex3f(-300, -300, 0) 104 | 105 | glEnd() 106 | 107 | pygame.display.flip() 108 | 109 | glDeleteTextures(texture_id) 110 | 111 | if __name__ == "__main__": 112 | run() 113 | -------------------------------------------------------------------------------- /Chapter 11/11-2.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | SCREEN_SIZE = (800, 600) 10 | 11 | def resize(width, height): 12 | 13 | glViewport(0, 0, width, height) 14 | glMatrixMode(GL_PROJECTION) 15 | glLoadIdentity() 16 | gluPerspective(60.0, float(width)/height, .1, 1000.) 17 | glMatrixMode(GL_MODELVIEW) 18 | glLoadIdentity() 19 | 20 | def init(): 21 | 22 | glEnable(GL_TEXTURE_2D) 23 | glClearColor(1.0, 1.0, 1.0, 0.0) 24 | 25 | def run(): 26 | 27 | pygame.init() 28 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 29 | 30 | resize(*SCREEN_SIZE) 31 | init() 32 | 33 | # Load the textures 34 | texture_surface = pygame.image.load("sushitex.png") 35 | # Retrieve the texture data 36 | texture_data = pygame.image.tostring(texture_surface, 'RGB', True) 37 | 38 | # Generate a texture id 39 | texture_id = glGenTextures(1) 40 | # Tell OpenGL we will be using this texture id for texture operations 41 | glBindTexture(GL_TEXTURE_2D, texture_id) 42 | 43 | # Tell OpenGL how to scale images 44 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) 45 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ) 46 | 47 | # Tell OpenGL that data is aligned to byte boundries 48 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1) 49 | 50 | # Get the dimensions of the image 51 | width, height = texture_surface.get_rect().size 52 | 53 | # Upload the image to OpenGL 54 | glTexImage2D( GL_TEXTURE_2D, 55 | 0, 56 | 3, 57 | width, 58 | height, 59 | 0, 60 | GL_RGB, 61 | GL_UNSIGNED_BYTE, 62 | texture_data) 63 | 64 | 65 | clock = pygame.time.Clock() 66 | 67 | tex_rotation = 0.0 68 | 69 | while True: 70 | 71 | for event in pygame.event.get(): 72 | if event.type == QUIT: 73 | pygame.quit() 74 | quit() 75 | 76 | 77 | time_passed = clock.tick() 78 | time_passed_seconds = time_passed / 1000. 79 | tex_rotation += time_passed_seconds * 360.0 / 8.0 80 | 81 | 82 | # Clear the screen (similar to fill) 83 | glClear(GL_COLOR_BUFFER_BIT) 84 | 85 | # Clear the model-view matrix 86 | glLoadIdentity() 87 | 88 | # Set the modelview matrix 89 | glTranslatef(0.0, 0.0, -600.0) 90 | glRotate(tex_rotation, 1, 0, 0) 91 | 92 | # Draw a quad (4 vertices, 4 texture coords) 93 | glBegin(GL_QUADS) 94 | 95 | glTexCoord2f(0, 1) 96 | glVertex3f(-300, 300, 0) 97 | 98 | glTexCoord2f(1, 1) 99 | glVertex3f(300, 300, 0) 100 | 101 | glTexCoord2f(1, 0) 102 | glVertex3f(300, -300, 0) 103 | 104 | glTexCoord2f(0, 0) 105 | glVertex3f(-300, -300, 0) 106 | 107 | glEnd() 108 | 109 | pygame.display.flip() 110 | 111 | glDeleteTextures(texture_id) 112 | 113 | if __name__ == "__main__": 114 | run() 115 | -------------------------------------------------------------------------------- /Chapter 11/11-3.py: -------------------------------------------------------------------------------- 1 | from OpenGL.GL import * 2 | from OpenGL.GLU import * 3 | 4 | import pygame 5 | import os.path 6 | 7 | class Material(object): 8 | 9 | def init (self): 10 | 11 | self.name = "" 12 | self.texture_fname = None 13 | self.texture_id = None 14 | 15 | class FaceGroup(object): 16 | 17 | def init (self): 18 | 19 | self.tri_indices = [] 20 | self.material_name = "" 21 | 22 | class Model3D(object): 23 | 24 | def init (self): 25 | 26 | self.vertices = [] 27 | self.tex_coords = [] 28 | self.normals = [] 29 | self.materials = {} 30 | self.face_groups = [] 31 | # Display list id for quick rendering 32 | self.display_list_id = None 33 | -------------------------------------------------------------------------------- /Chapter 11/11-4.py: -------------------------------------------------------------------------------- 1 | def read_obj(self, fname): 2 | 3 | current_face_group = None 4 | 5 | file_in = open(fname) 6 | 7 | for line in file_in: 8 | 9 | # Parse command and data from each line 10 | words = line.split() 11 | command = words[0] 12 | data = words[1:] 13 | 14 | if command == 'mtllib': # Material library 15 | 16 | model_path = os.path.split(fname)[0] 17 | mtllib_path = os.path.join( model_path, data[0] ) 18 | self.read_mtllib(mtllib_path) 19 | 20 | elif command == 'v': # Vertex 21 | x, y, z = data 22 | vertex = (float(x), float(y), float(z)) 23 | self.vertices.append(vertex) 24 | 25 | elif command == 'vt': # Texture coordinate 26 | 27 | s, t = data 28 | tex_coord = (float(s), float(t)) 29 | self.tex_coords.append(tex_coord) 30 | 31 | elif command == 'vn': # Normal 32 | 33 | x, y, z = data 34 | normal = (float(x), float(y), float(z)) 35 | self.normals.append(normal) 36 | 37 | elif command == 'usemtl' : # Use material 38 | 39 | current_face_group = FaceGroup() 40 | current_face_group.material_name = data[0] 41 | self.face_groups.append( current_face_group ) 42 | 43 | elif command == 'f': 44 | 45 | assert len(data) == 3, "Sorry, only triangles are supported" 46 | 47 | # Parse indices from triples 48 | for word in data: 49 | vi, ti, ni = word.split('/') 50 | indices = (int(vi) - 1, int(ti) - 1, int(ni) - 1) 51 | current_face_group.tri_indices.append(indices) 52 | 53 | 54 | for material in self.materials.values(): 55 | 56 | model_path = os.path.split(fname)[0] 57 | texture_path = os.path.join(model_path, material.texture_fname) 58 | texture_surface = pygame.image.load(texture_path) 59 | texture_data = pygame.image.tostring(texture_surface, 'RGB', True) 60 | 61 | material.texture_id = glGenTextures(1) 62 | glBindTexture(GL_TEXTURE_2D, material.texture_id) 63 | 64 | glTexParameteri( GL_TEXTURE_2D, 65 | GL_TEXTURE_MAG_FILTER, 66 | GL_LINEAR) 67 | glTexParameteri( GL_TEXTURE_2D, 68 | GL_TEXTURE_MIN_FILTER, 69 | GL_LINEAR_MIPMAP_LINEAR) 70 | 71 | glPixelStorei(GL_UNPACK_ALIGNMENT,1) 72 | width, height = texture_surface.get_rect().size 73 | gluBuild2DMipmaps( GL_TEXTURE_2D, 74 | 3, 75 | width, 76 | height, 77 | GL_RGB, 78 | GL_UNSIGNED_BYTE, 79 | texture_data) 80 | -------------------------------------------------------------------------------- /Chapter 11/11-5.py: -------------------------------------------------------------------------------- 1 | def read_mtllib(self, mtl_fname): 2 | 3 | file_mtllib = open(mtl_fname) 4 | for line in file_mtllib: 5 | 6 | words = line.split() 7 | command = words[0] 8 | data = words[1:] 9 | 10 | if command == 'newmtl': 11 | material = Material() 12 | material.name = data[0] 13 | self.materials[data[0]] = material 14 | 15 | elif command == 'map_Kd': 16 | material.texture_fname = data[0] 17 | -------------------------------------------------------------------------------- /Chapter 11/11-6.py: -------------------------------------------------------------------------------- 1 | def draw(self): 2 | 3 | vertices = self.vertices 4 | tex_coords = self.tex_coords 5 | normals = self.normals 6 | 7 | for face_group in self.face_groups: 8 | 9 | material = self.materials[face_group.material_name] 10 | glBindTexture(GL_TEXTURE_2D, material.texture_id) 11 | 12 | glBegin(GL_TRIANGLES) 13 | for vi, ti, ni in face_group.tri_indices: 14 | glTexCoord2fv( tex_coords[ti] ) 15 | glNormal3fv( normals[ni] ) 16 | glVertex3fv( vertices[vi] ) 17 | glEnd() 18 | 19 | 20 | def draw_quick(self): 21 | 22 | if self.display_list_id is None: 23 | self.display_list_id = glGenLists(1) 24 | glNewList(self.display_list_id, GL_COMPILE) 25 | self.draw() 26 | glEndList() 27 | 28 | glCallList(self.display_list_id) 29 | -------------------------------------------------------------------------------- /Chapter 11/11-7.py: -------------------------------------------------------------------------------- 1 | def __del__(self): 2 | 3 | #Called when the model is cleaned up by Python 4 | self.free_resources() 5 | 6 | def free_resources(self): 7 | 8 | # Delete the display list and textures 9 | if self.display_list_id is not None: 10 | glDeleteLists(self.display_list_id, 1) 11 | self.display_list_id = None 12 | 13 | # Delete any textures we used 14 | for material in self.materials.values(): 15 | if material.texture_id is not None: 16 | glDeleteTextures(material.texture_id) 17 | 18 | # Clear all the materials 19 | self.materials.clear() 20 | 21 | # Clear the geometry lists 22 | del self.vertices[:] 23 | del self.tex_coords[:] 24 | del self.normals[:] 25 | del self.face_groups[:] 26 | -------------------------------------------------------------------------------- /Chapter 11/11-8.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | # Import the Model3D class 10 | import model3d 11 | 12 | SCREEN_SIZE = (800, 600) 13 | 14 | def resize(width, height): 15 | 16 | glViewport(0, 0, width, height) 17 | glMatrixMode(GL_PROJECTION) 18 | glLoadIdentity() 19 | gluPerspective(60.0, float(width)/height, .1, 1000.) 20 | glMatrixMode(GL_MODELVIEW) 21 | glLoadIdentity() 22 | 23 | 24 | def init(): 25 | 26 | # Enable the GL features we will be using 27 | glEnable(GL_DEPTH_TEST) 28 | glEnable(GL_LIGHTING) 29 | glEnable(GL_COLOR_MATERIAL) 30 | glEnable(GL_TEXTURE_2D) 31 | glEnable(GL_CULL_FACE) 32 | 33 | glShadeModel(GL_SMOOTH) 34 | glClearColor(1.0, 1.0, 1.0, 0.0) # white 35 | 36 | # Set the material 37 | glMaterial(GL_FRONT, GL_AMBIENT, (0.0, 0.0, 0.0, 1.0)) 38 | glMaterial(GL_FRONT, GL_DIFFUSE, (0.2, 0.2, 0.2, 1.0)) 39 | glMaterial(GL_FRONT, GL_SPECULAR, (1.0, 1.0, 1.0, 1.0)) 40 | glMaterial(GL_FRONT, GL_SHININESS, 10.0) 41 | 42 | # Set light parameters 43 | glLight(GL_LIGHT0, GL_AMBIENT, (0.0, 0.0, 0.0, 1.0)) 44 | glLight(GL_LIGHT0, GL_DIFFUSE, (0.4, 0.4, 0.4, 1.0)) 45 | glLight(GL_LIGHT0, GL_SPECULAR, (1.0, 1.0, 1.0, 1.0)) 46 | 47 | # Enable light 1 and set position 48 | glEnable(GL_LIGHT0) 49 | glLight(GL_LIGHT0, GL_POSITION, (0, .5, 1, 0)) 50 | 51 | 52 | def run(): 53 | 54 | pygame.init() 55 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 56 | 57 | resize(*SCREEN_SIZE) 58 | init() 59 | 60 | clock = pygame.time.Clock() 61 | 62 | # Read the model 63 | tank_model = model3d.Model3D() 64 | tank_model.read_obj('mytank.obj') 65 | 66 | rotation = 0.0 67 | 68 | while True: 69 | 70 | for event in pygame.event.get(): 71 | if event.type == QUIT: 72 | pygame.quit() 73 | quit() 74 | 75 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 76 | 77 | time_passed = clock.tick() 78 | time_passed_seconds = time_passed / 1000.0 79 | 80 | glLoadIdentity() 81 | glRotatef(15, 1, 0, 0) 82 | glTranslatef(0.0, -1.5, -3.5) 83 | 84 | rotation += time_passed_seconds * 45.0 85 | glRotatef(rotation, 0, 1, 0) 86 | 87 | tank_model.draw_quick() 88 | 89 | pygame.display.flip() 90 | 91 | 92 | if __name__ == "__main__": 93 | run() 94 | -------------------------------------------------------------------------------- /Chapter 11/bodytex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 11/bodytex.jpg -------------------------------------------------------------------------------- /Chapter 11/booktex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 11/booktex.png -------------------------------------------------------------------------------- /Chapter 11/gameobjects/__pycache__/matrix44.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 11/gameobjects/__pycache__/matrix44.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 11/gameobjects/__pycache__/util.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 11/gameobjects/__pycache__/util.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 11/gameobjects/__pycache__/vector2.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 11/gameobjects/__pycache__/vector2.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 11/gameobjects/__pycache__/vector3.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 11/gameobjects/__pycache__/vector3.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 11/gameobjects/util.py: -------------------------------------------------------------------------------- 1 | from math import * 2 | 3 | def format_number(n, accuracy=6): 4 | """Formats a number in a friendly manner 5 | (removes trailing zeros and unneccesary point.""" 6 | 7 | fs = "%."+str(accuracy)+"f" 8 | str_n = fs%float(n) 9 | if '.' in str_n: 10 | str_n = str_n.rstrip('0').rstrip('.') 11 | if str_n == "-0": 12 | str_n = "0" 13 | #str_n = str_n.replace("-0", "0") 14 | return str_n 15 | 16 | 17 | def lerp(a, b, i): 18 | """Linear enterpolate from a to b.""" 19 | return a+(b-a)*i 20 | 21 | 22 | def range2d(range_x, range_y): 23 | 24 | """Creates a 2D range.""" 25 | 26 | range_x = list(range_x) 27 | return [ (x, y) for y in range_y for x in range_x ] 28 | 29 | 30 | def xrange2d(range_x, range_y): 31 | 32 | """Iterates over a 2D range.""" 33 | 34 | range_x = list(range_x) 35 | for y in range_y: 36 | for x in range_x: 37 | yield (x, y) 38 | 39 | 40 | def saturate(value, low, high): 41 | return min(max(value, low), high) 42 | 43 | 44 | def is_power_of_2(n): 45 | """Returns True if a value is a power of 2.""" 46 | return log(n, 2) % 1.0 == 0.0 47 | 48 | 49 | def next_power_of_2(n): 50 | """Returns the next power of 2 that is >= n""" 51 | return int(2 ** ceil(log(n, 2))) 52 | 53 | if __name__ == "__main__": 54 | 55 | print(list( xrange2d(range(3), range(3)) )) 56 | print(range2d(range(3), range(3))) 57 | print(is_power_of_2(7)) 58 | print(is_power_of_2(8)) 59 | print(is_power_of_2(9)) 60 | 61 | print(next_power_of_2(7)) 62 | -------------------------------------------------------------------------------- /Chapter 11/model3d.py: -------------------------------------------------------------------------------- 1 | 2 | from OpenGL.GL import * 3 | from OpenGL.GLU import * 4 | 5 | import pygame 6 | import os.path 7 | 8 | class Material(object): 9 | 10 | def __init__(self): 11 | 12 | self.name = "" 13 | self.texture_fname = None 14 | self.texture_id = None 15 | 16 | 17 | class FaceGroup(object): 18 | 19 | def __init__(self): 20 | 21 | self.tri_indices = [] 22 | self.material_name = "" 23 | 24 | 25 | class Model3D(object): 26 | 27 | def __init__(self): 28 | 29 | self.vertices = [] 30 | self.tex_coords = [] 31 | self.normals = [] 32 | self.materials = {} 33 | self.face_groups = [] 34 | self.display_list_id = None 35 | 36 | def __del__(self): 37 | 38 | #Called when the model is cleaned up by Python 39 | self.free_resources() 40 | 41 | def free_resources(self): 42 | 43 | # Delete the display list and textures 44 | if self.display_list_id is not None: 45 | glDeleteLists(self.display_list_id, 1) 46 | self.display_list_id = None 47 | 48 | # Delete any textures we used 49 | for material in self.materials.values(): 50 | if material.texture_id is not None: 51 | glDeleteTextures(material.texture_id) 52 | 53 | # Clear all the materials 54 | self.materials.clear() 55 | 56 | # Clear the geometry lists 57 | del self.vertices[:] 58 | del self.tex_coords[:] 59 | del self.normals[:] 60 | del self.face_groups[:] 61 | 62 | 63 | 64 | def read_obj(self, fname): 65 | 66 | current_face_group = None 67 | 68 | file_in = open(fname) 69 | 70 | for line in file_in: 71 | 72 | # Parse command and data from each line 73 | words = line.split() 74 | command = words[0] 75 | data = words[1:] 76 | 77 | if command == 'mtllib': # Material library 78 | 79 | model_path = os.path.split(fname)[0] 80 | mtllib_path = os.path.join( model_path, data[0] ) 81 | self.read_mtllib(mtllib_path) 82 | 83 | elif command == 'v': # Vertex 84 | x, y, z = data 85 | vertex = (float(x), float(y), float(z)) 86 | self.vertices.append(vertex) 87 | 88 | elif command == 'vt': # Texture coordinate 89 | 90 | s, t = data 91 | tex_coord = (float(s), float(t)) 92 | self.tex_coords.append(tex_coord) 93 | 94 | elif command == 'vn': # Normal 95 | 96 | x, y, z = data 97 | normal = (float(x), float(y), float(z)) 98 | self.normals.append(normal) 99 | 100 | elif command == 'usemtl' : # Use material 101 | 102 | current_face_group = FaceGroup() 103 | current_face_group.material_name = data[0] 104 | self.face_groups.append( current_face_group ) 105 | 106 | elif command == 'f': 107 | 108 | assert len(data) == 3, "Sorry, only triangles are supported" 109 | 110 | # Parse indices from triples 111 | for word in data: 112 | vi, ti, ni = word.split('/') 113 | indices = (int(vi) - 1, int(ti) - 1, int(ni) - 1) 114 | current_face_group.tri_indices.append(indices) 115 | 116 | 117 | for material in self.materials.values(): 118 | 119 | model_path = os.path.split(fname)[0] 120 | texture_path = os.path.join(model_path, material.texture_fname) 121 | texture_surface = pygame.image.load(texture_path) 122 | texture_data = pygame.image.tostring(texture_surface, 'RGB', True) 123 | 124 | material.texture_id = glGenTextures(1) 125 | glBindTexture(GL_TEXTURE_2D, material.texture_id) 126 | 127 | glTexParameteri( GL_TEXTURE_2D, 128 | GL_TEXTURE_MAG_FILTER, 129 | GL_LINEAR) 130 | glTexParameteri( GL_TEXTURE_2D, 131 | GL_TEXTURE_MIN_FILTER, 132 | GL_LINEAR_MIPMAP_LINEAR) 133 | 134 | glPixelStorei(GL_UNPACK_ALIGNMENT,1) 135 | width, height = texture_surface.get_rect().size 136 | gluBuild2DMipmaps( GL_TEXTURE_2D, 137 | 3, 138 | width, 139 | height, 140 | GL_RGB, 141 | GL_UNSIGNED_BYTE, 142 | texture_data) 143 | 144 | 145 | def read_mtllib(self, mtl_fname): 146 | 147 | file_mtllib = open(mtl_fname) 148 | for line in file_mtllib: 149 | 150 | words = line.split() 151 | command = words[0] 152 | data = words[1:] 153 | 154 | if command == 'newmtl': 155 | material = Material() 156 | material.name = data[0] 157 | self.materials[data[0]] = material 158 | 159 | elif command == 'map_Kd': 160 | material.texture_fname = data[0] 161 | 162 | 163 | def draw(self): 164 | 165 | vertices = self.vertices 166 | tex_coords = self.tex_coords 167 | normals = self.normals 168 | 169 | for face_group in self.face_groups: 170 | 171 | material = self.materials[face_group.material_name] 172 | glBindTexture(GL_TEXTURE_2D, material.texture_id) 173 | 174 | glBegin(GL_TRIANGLES) 175 | for vi, ti, ni in face_group.tri_indices: 176 | glTexCoord2fv( tex_coords[ti] ) 177 | glNormal3fv( normals[ni] ) 178 | glVertex3fv( vertices[vi] ) 179 | glEnd() 180 | 181 | 182 | def draw_quick(self): 183 | 184 | if self.display_list_id is None: 185 | self.display_list_id = glGenLists(1) 186 | glNewList(self.display_list_id, GL_COMPILE) 187 | self.draw() 188 | glEndList() 189 | 190 | glCallList(self.display_list_id) 191 | -------------------------------------------------------------------------------- /Chapter 11/mytank.mtl: -------------------------------------------------------------------------------- 1 | newmtl acmat_0 2 | Kd 1 1 1 3 | Ka 0.2 0.2 0.2 4 | Ks 0.2 0.2 0.2 5 | Ns 128 6 | Tr 0 7 | map_Kd booktex.png 8 | newmtl acmat_1 9 | Kd 1 1 1 10 | Ka 0.2 0.2 0.2 11 | Ks 0.2 0.2 0.2 12 | Ns 128 13 | Tr 0 14 | map_Kd bodytex.jpg 15 | newmtl acmat_2 16 | Kd 1 1 1 17 | Ka 0.2 0.2 0.2 18 | Ks 0.2 0.2 0.2 19 | Ns 128 20 | Tr 0 21 | map_Kd mytanktex.jpg 22 | -------------------------------------------------------------------------------- /Chapter 11/mytanktex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 11/mytanktex.jpg -------------------------------------------------------------------------------- /Chapter 11/opengltex.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | SCREEN_SIZE = (800, 600) 10 | 11 | def resize(width, height): 12 | 13 | glViewport(0, 0, width, height) 14 | glMatrixMode(GL_PROJECTION) 15 | glLoadIdentity() 16 | gluPerspective(60.0, float(width)/height, .1, 1000.) 17 | glMatrixMode(GL_MODELVIEW) 18 | glLoadIdentity() 19 | 20 | def init(): 21 | 22 | glEnable(GL_TEXTURE_2D) 23 | glClearColor(1.0, 1.0, 1.0, 0.0) 24 | 25 | def run(): 26 | 27 | pygame.init() 28 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 29 | 30 | resize(*SCREEN_SIZE) 31 | init() 32 | 33 | # Load the textures 34 | texture_surface = pygame.image.load("sushitex.png") 35 | # Retrieve the texture data 36 | texture_data = pygame.image.tostring(texture_surface, 'RGB', True) 37 | 38 | # Generate a texture id 39 | texture_id = glGenTextures(1) 40 | # Tell OpenGL we will be using this texture id for texture operations 41 | glBindTexture(GL_TEXTURE_2D, texture_id) 42 | 43 | # Tell OpenGL how to scale images 44 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) 45 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ) 46 | 47 | # Tell OpenGL that data is aligned to byte boundries 48 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1) 49 | 50 | # Get the dimensions of the image 51 | width, height = texture_surface.get_rect().size 52 | 53 | # Upload the image to OpenGL 54 | glTexImage2D( GL_TEXTURE_2D, 55 | 0, 56 | 3, 57 | width, 58 | height, 59 | 0, 60 | GL_RGB, 61 | GL_UNSIGNED_BYTE, 62 | texture_data) 63 | 64 | 65 | clock = pygame.time.Clock() 66 | 67 | tex_rotation = 0.0 68 | 69 | while True: 70 | 71 | for event in pygame.event.get(): 72 | if event.type == QUIT: 73 | pygame.quit() 74 | quit() 75 | 76 | 77 | time_passed = clock.tick() 78 | time_passed_seconds = time_passed / 1000. 79 | tex_rotation += time_passed_seconds * 360.0 / 8.0 80 | 81 | 82 | # Clear the screen (similar to fill) 83 | glClear(GL_COLOR_BUFFER_BIT) 84 | 85 | # Clear the model-view matrix 86 | glLoadIdentity() 87 | 88 | # Set the modelview matrix 89 | glTranslatef(0.0, 0.0, -600.0) 90 | glRotate(tex_rotation, 1, 0, 0) 91 | 92 | # Draw a quad (4 vertices, 4 texture coords) 93 | glBegin(GL_QUADS) 94 | 95 | glTexCoord2f(0, 1) 96 | glVertex3f(-300, 300, 0) 97 | 98 | glTexCoord2f(1, 1) 99 | glVertex3f(300, 300, 0) 100 | 101 | glTexCoord2f(1, 0) 102 | glVertex3f(300, -300, 0) 103 | 104 | glTexCoord2f(0, 0) 105 | glVertex3f(-300, -300, 0) 106 | 107 | glEnd() 108 | 109 | pygame.display.flip() 110 | 111 | glDeleteTextures(texture_id) 112 | 113 | if __name__ == "__main__": 114 | run() 115 | -------------------------------------------------------------------------------- /Chapter 11/opengltextiled.py: -------------------------------------------------------------------------------- 1 | SCREEN_SIZE = (800, 600) 2 | 3 | from math import radians 4 | 5 | from OpenGL.GL import * 6 | from OpenGL.GLU import * 7 | 8 | import pygame 9 | from pygame.locals import * 10 | 11 | 12 | def resize(width, height): 13 | 14 | glViewport(0, 0, width, height) 15 | glMatrixMode(GL_PROJECTION) 16 | glLoadIdentity() 17 | gluPerspective(60.0, float(width)/height, .1, 1000.) 18 | glMatrixMode(GL_MODELVIEW) 19 | glLoadIdentity() 20 | 21 | def init(): 22 | 23 | glEnable(GL_TEXTURE_2D) 24 | glClearColor(1.0, 1.0, 1.0, 0.0) 25 | 26 | def run(): 27 | 28 | pygame.init() 29 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 30 | 31 | resize(*SCREEN_SIZE) 32 | init() 33 | 34 | # Load the textures 35 | texture_surface = pygame.image.load("sushitex.png") 36 | # Retrieve the texture data 37 | texture_data = pygame.image.tostring(texture_surface, 'RGB', True) 38 | 39 | # Generate a texture id 40 | texture_id = glGenTextures(1) 41 | # Tell OpenGL we will be using this texture id for texture operations 42 | glBindTexture(GL_TEXTURE_2D, texture_id) 43 | 44 | # Tell OpenGL how to scale images 45 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) 46 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ) 47 | 48 | # Tell OpenGL that data is aligned to byte boundries 49 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1) 50 | 51 | # Get the dimensions of the image 52 | width, height = texture_surface.get_rect().size 53 | 54 | # Upload the image to OpenGL 55 | glTexImage2D( GL_TEXTURE_2D, 56 | 0, 57 | 3, 58 | width, 59 | height, 60 | 0, 61 | GL_RGB, 62 | GL_UNSIGNED_BYTE, 63 | texture_data) 64 | 65 | 66 | clock = pygame.time.Clock() 67 | 68 | tex_rotation = 0.0 69 | 70 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 71 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 72 | 73 | while True: 74 | 75 | for event in pygame.event.get(): 76 | if event.type == QUIT: 77 | pygame.quit() 78 | quit() 79 | 80 | 81 | time_passed = clock.tick() 82 | time_passed_seconds = time_passed / 1000. 83 | tex_rotation += time_passed_seconds * 360.0 / 8.0 84 | 85 | 86 | # Clear the screen (similar to fill) 87 | glClear(GL_COLOR_BUFFER_BIT) 88 | 89 | # Clear the model-view matrix 90 | glLoadIdentity() 91 | 92 | # Set the modelview matrix 93 | glTranslatef(0.0, 0.0, -600.0) 94 | glRotate(tex_rotation, 1, 0, 0) 95 | 96 | # Draw a quad (4 vertices, 4 texture coords) 97 | glBegin(GL_QUADS) 98 | 99 | glTexCoord2f(0, 3) 100 | glVertex3f(-300, 300, 0) 101 | 102 | glTexCoord2f(3, 3) 103 | glVertex3f(300, 300, 0) 104 | 105 | glTexCoord2f(3, 0) 106 | glVertex3f(300, -300, 0) 107 | 108 | glTexCoord2f(0, 0) 109 | glVertex3f(-300, -300, 0) 110 | 111 | glEnd() 112 | 113 | pygame.display.flip() 114 | 115 | glDeleteTextures(texture_id) 116 | 117 | if __name__ == "__main__": 118 | run() 119 | -------------------------------------------------------------------------------- /Chapter 11/sushitex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 11/sushitex.png -------------------------------------------------------------------------------- /Chapter 11/tankdemo.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | # Import the Model3D class 10 | import model3d 11 | 12 | SCREEN_SIZE = (800, 600) 13 | 14 | def resize(width, height): 15 | 16 | glViewport(0, 0, width, height) 17 | glMatrixMode(GL_PROJECTION) 18 | glLoadIdentity() 19 | gluPerspective(60.0, float(width)/height, .1, 1000.) 20 | glMatrixMode(GL_MODELVIEW) 21 | glLoadIdentity() 22 | 23 | 24 | def init(): 25 | 26 | # Enable the GL features we will be using 27 | glEnable(GL_DEPTH_TEST) 28 | glEnable(GL_LIGHTING) 29 | glEnable(GL_COLOR_MATERIAL) 30 | glEnable(GL_TEXTURE_2D) 31 | glEnable(GL_CULL_FACE) 32 | 33 | glShadeModel(GL_SMOOTH) 34 | glClearColor(1.0, 1.0, 1.0, 0.0) # white 35 | 36 | # Set the material 37 | glMaterial(GL_FRONT, GL_AMBIENT, (0.0, 0.0, 0.0, 1.0)) 38 | glMaterial(GL_FRONT, GL_DIFFUSE, (0.2, 0.2, 0.2, 1.0)) 39 | glMaterial(GL_FRONT, GL_SPECULAR, (1.0, 1.0, 1.0, 1.0)) 40 | glMaterial(GL_FRONT, GL_SHININESS, 10.0) 41 | 42 | # Set light parameters 43 | glLight(GL_LIGHT0, GL_AMBIENT, (0.0, 0.0, 0.0, 1.0)) 44 | glLight(GL_LIGHT0, GL_DIFFUSE, (0.4, 0.4, 0.4, 1.0)) 45 | glLight(GL_LIGHT0, GL_SPECULAR, (1.0, 1.0, 1.0, 1.0)) 46 | 47 | # Enable light 1 and set position 48 | glEnable(GL_LIGHT0) 49 | glLight(GL_LIGHT0, GL_POSITION, (0, .5, 1, 0)) 50 | 51 | 52 | def run(): 53 | 54 | pygame.init() 55 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 56 | 57 | resize(*SCREEN_SIZE) 58 | init() 59 | 60 | clock = pygame.time.Clock() 61 | 62 | # Read the model 63 | tank_model = model3d.Model3D() 64 | tank_model.read_obj('mytank.obj') 65 | 66 | rotation = 0.0 67 | 68 | while True: 69 | 70 | for event in pygame.event.get(): 71 | if event.type == QUIT: 72 | pygame.quit() 73 | quit() 74 | 75 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 76 | 77 | time_passed = clock.tick() 78 | time_passed_seconds = time_passed / 1000.0 79 | 80 | glLoadIdentity() 81 | glRotatef(15, 1, 0, 0) 82 | glTranslatef(0.0, -1.5, -3.5) 83 | 84 | rotation += time_passed_seconds * 45.0 85 | glRotatef(rotation, 0, 1, 0) 86 | 87 | tank_model.draw_quick() 88 | 89 | pygame.display.flip() 90 | 91 | 92 | if __name__ == "__main__": 93 | run() 94 | -------------------------------------------------------------------------------- /Chapter 12/12-1.py: -------------------------------------------------------------------------------- 1 | def get_attenuation(distance, constant, linear, quadratic): 2 | return 1.0 / (constant + linear * distance + quadratic * (distance ** 2)) 3 | -------------------------------------------------------------------------------- /Chapter 12/12-2.py: -------------------------------------------------------------------------------- 1 | def alpha_blend(src, dst): 2 | return src * src.a + dst * (1.0—src.a) 3 | -------------------------------------------------------------------------------- /Chapter 12/12-3.py: -------------------------------------------------------------------------------- 1 | def additive_blend(src, dst): 2 | return src * src.a + dst 3 | -------------------------------------------------------------------------------- /Chapter 12/12-4.py: -------------------------------------------------------------------------------- 1 | def subtractive_blend(src, dst): 2 | return dst—src * src.a 3 | -------------------------------------------------------------------------------- /Chapter 12/12-5.py: -------------------------------------------------------------------------------- 1 | from OpenGL.GL import * 2 | from OpenGL.GLU import * 3 | 4 | import pygame 5 | from pygame.locals import * 6 | 7 | SCREEN_SIZE = (800, 600) 8 | 9 | def resize(width, height): 10 | 11 | glViewport(0, 0, width, height) 12 | glMatrixMode(GL_PROJECTION) 13 | glLoadIdentity() 14 | gluPerspective(45.0, float(width)/height, .1, 1000.) 15 | glMatrixMode(GL_MODELVIEW) 16 | glLoadIdentity() 17 | 18 | 19 | def init(): 20 | 21 | glEnable(GL_TEXTURE_2D) 22 | glEnable(GL_BLEND) 23 | glClearColor(1.0, 1.0, 1.0, 0.0) 24 | 25 | 26 | def upload_texture(filename, use_alpha=False): 27 | 28 | # Read an image file and upload a texture 29 | if use_alpha: 30 | format, gl_format, bits_per_pixel = 'RGBA', GL_RGBA, 4 31 | else: 32 | format, gl_format, bits_per_pixel = 'RGB', GL_RGB, 3 33 | 34 | img_surface = pygame.image.load(filename) 35 | 36 | #img_surface = premul_surface(img_surface) 37 | 38 | data = pygame.image.tostring(img_surface, format, True) 39 | 40 | texture_id = glGenTextures(1) 41 | 42 | glBindTexture(GL_TEXTURE_2D, texture_id) 43 | 44 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) 45 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ) 46 | 47 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1) 48 | 49 | width, height = img_surface.get_rect().size 50 | 51 | glTexImage2D( GL_TEXTURE_2D, 52 | 0, 53 | bits_per_pixel, 54 | width, 55 | height, 56 | 0, 57 | gl_format, 58 | GL_UNSIGNED_BYTE, 59 | data ) 60 | 61 | # Return the texture id, so we can use glBindTexture 62 | return texture_id 63 | 64 | 65 | def draw_quad(x, y, z, w, h): 66 | 67 | # Send four vertices to draw a quad 68 | glBegin(GL_QUADS) 69 | 70 | glTexCoord2f(0, 0) 71 | glVertex3f(x-w/2, y-h/2, z) 72 | 73 | glTexCoord2f(1, 0) 74 | glVertex3f(x+w/2, y-h/2, z) 75 | 76 | glTexCoord2f(1, 1) 77 | glVertex3f(x+w/2, y+h/2, z) 78 | 79 | glTexCoord2f(0, 1) 80 | glVertex3f(x-w/2, y+h/2, z) 81 | 82 | glEnd() 83 | 84 | 85 | def run(): 86 | 87 | pygame.init() 88 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 89 | 90 | resize(*SCREEN_SIZE) 91 | init() 92 | 93 | # Upload the background and fugu texture 94 | background_tex = upload_texture('background.png') 95 | fugu_tex = upload_texture('fugu.png', True) 96 | 97 | while True: 98 | 99 | for event in pygame.event.get(): 100 | if event.type == QUIT: 101 | pygame.quit() 102 | quit() 103 | if event.type == KEYDOWN: 104 | if event.key == K_1: 105 | # Simple alpha blending 106 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 107 | glBlendEquation(GL_FUNC_ADD) 108 | elif event.key == K_2: 109 | # Additive alpha blending 110 | glBlendFunc(GL_SRC_ALPHA, GL_ONE) 111 | glBlendEquation(GL_FUNC_ADD) 112 | elif event.key == K_3: 113 | # Subtractive blending 114 | glBlendFunc(GL_SRC_ALPHA, GL_ONE) 115 | glBlendEquation(GL_FUNC_REVERSE_SUBTRACT) 116 | 117 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 118 | 119 | # Draw the background 120 | glBindTexture(GL_TEXTURE_2D, background_tex) 121 | 122 | glDisable(GL_BLEND) 123 | draw_quad(0, 0, -SCREEN_SIZE[1], 600, 600) 124 | glEnable(GL_BLEND) 125 | 126 | # Draw a texture at the mouse position 127 | glBindTexture(GL_TEXTURE_2D, fugu_tex) 128 | x, y = pygame.mouse.get_pos() 129 | x -= SCREEN_SIZE[0]/2 130 | y -= SCREEN_SIZE[1]/2 131 | draw_quad(x, -y, -SCREEN_SIZE[1], 256, 256) 132 | 133 | pygame.display.flip() 134 | 135 | # Free the textures we used 136 | glDeleteTextures(background_tex) 137 | glDeleteTextures(fugu_tex) 138 | 139 | if __name__ == "__main__": 140 | run() 141 | -------------------------------------------------------------------------------- /Chapter 12/12-6.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | # Import the Model3D class 10 | import model3d 11 | 12 | SCREEN_SIZE = (800, 600) 13 | 14 | def resize(width, height): 15 | 16 | glViewport(0, 0, width, height) 17 | glMatrixMode(GL_PROJECTION) 18 | glLoadIdentity() 19 | gluPerspective(60.0, float(width)/height, .1, 1000.) 20 | glMatrixMode(GL_MODELVIEW) 21 | glLoadIdentity() 22 | 23 | 24 | def init(): 25 | 26 | # Enable the GL features we will be using 27 | glEnable(GL_DEPTH_TEST) 28 | glEnable(GL_LIGHTING) 29 | glEnable(GL_TEXTURE_2D) 30 | glShadeModel(GL_SMOOTH) 31 | 32 | # Enable light 1 and set position 33 | glEnable(GL_LIGHTING) 34 | glEnable(GL_LIGHT0) 35 | glLight(GL_LIGHT0, GL_POSITION, (0, .5, 1)) 36 | 37 | 38 | def run(): 39 | 40 | pygame.init() 41 | screen = pygame.display.set_mode(SCREEN_SIZE, FULLSCREEN|HWSURFACE|OPENGL|DOUBLEBUF) 42 | 43 | resize(*SCREEN_SIZE) 44 | init() 45 | 46 | # Read the skybox model 47 | sky_box = model3d.Model3D() 48 | sky_box.read_obj('tanksky/skybox.obj') 49 | 50 | # Set the wraping mode of all textures in the sky-box to GL_CLAMP_TO_EDGE 51 | for material in sky_box.materials.values(): 52 | 53 | glBindTexture(GL_TEXTURE_2D, material.texture_id) 54 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 55 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 56 | 57 | 58 | # Used to rotate the world 59 | mouse_x = 0.0 60 | mouse_y = 0.0 61 | 62 | #Don't display the mouse cursor 63 | pygame.mouse.set_visible(False) 64 | 65 | while True: 66 | 67 | for event in pygame.event.get(): 68 | if event.type == QUIT: 69 | pygame.quit() 70 | quit() 71 | if event.type == KEYDOWN: 72 | pygame.quit() 73 | quit() 74 | 75 | # We don't need to clear the color buffer (GL_COLOR_BUFFER_BIT) 76 | # because the skybox covers the entire screen 77 | glClear(GL_DEPTH_BUFFER_BIT) 78 | 79 | glLoadIdentity() 80 | 81 | mouse_rel_x, mouse_rel_y = pygame.mouse.get_rel() 82 | mouse_x += float(mouse_rel_x) / 5.0 83 | mouse_y += float(mouse_rel_y) / 5.0 84 | 85 | # Rotate around the x and y axes to create a mouse-look camera 86 | glRotatef(mouse_y, 1, 0, 0) 87 | glRotatef(mouse_x, 0, 1, 0) 88 | 89 | # Disable lighting and depth test 90 | glDisable(GL_LIGHTING) 91 | glDepthMask(False) 92 | 93 | # Draw the skybox 94 | sky_box.draw_quick() 95 | 96 | # Re-enable lighting and depth test before we redraw the world 97 | glEnable(GL_LIGHTING) 98 | glDepthMask(True) 99 | 100 | # Here is where we would draw the rest of the world in a game 101 | 102 | pygame.display.flip() 103 | 104 | if __name__ == "__main__": 105 | run() 106 | 107 | 108 | -------------------------------------------------------------------------------- /Chapter 12/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/background.png -------------------------------------------------------------------------------- /Chapter 12/blenddemo.py: -------------------------------------------------------------------------------- 1 | from OpenGL.GL import * 2 | from OpenGL.GLU import * 3 | 4 | import pygame 5 | from pygame.locals import * 6 | 7 | SCREEN_SIZE = (800, 600) 8 | 9 | def resize(width, height): 10 | 11 | glViewport(0, 0, width, height) 12 | glMatrixMode(GL_PROJECTION) 13 | glLoadIdentity() 14 | gluPerspective(45.0, float(width)/height, .1, 1000.) 15 | glMatrixMode(GL_MODELVIEW) 16 | glLoadIdentity() 17 | 18 | 19 | def init(): 20 | 21 | glEnable(GL_TEXTURE_2D) 22 | glEnable(GL_BLEND) 23 | glClearColor(1.0, 1.0, 1.0, 0.0) 24 | 25 | 26 | def upload_texture(filename, use_alpha=False): 27 | 28 | # Read an image file and upload a texture 29 | if use_alpha: 30 | format, gl_format, bits_per_pixel = 'RGBA', GL_RGBA, 4 31 | else: 32 | format, gl_format, bits_per_pixel = 'RGB', GL_RGB, 3 33 | 34 | img_surface = pygame.image.load(filename) 35 | 36 | #img_surface = premul_surface(img_surface) 37 | 38 | data = pygame.image.tostring(img_surface, format, True) 39 | 40 | texture_id = glGenTextures(1) 41 | 42 | glBindTexture(GL_TEXTURE_2D, texture_id) 43 | 44 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) 45 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ) 46 | 47 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1) 48 | 49 | width, height = img_surface.get_rect().size 50 | 51 | glTexImage2D( GL_TEXTURE_2D, 52 | 0, 53 | bits_per_pixel, 54 | width, 55 | height, 56 | 0, 57 | gl_format, 58 | GL_UNSIGNED_BYTE, 59 | data ) 60 | 61 | # Return the texture id, so we can use glBindTexture 62 | return texture_id 63 | 64 | 65 | def draw_quad(x, y, z, w, h): 66 | 67 | # Send four vertices to draw a quad 68 | glBegin(GL_QUADS) 69 | 70 | glTexCoord2f(0, 0) 71 | glVertex3f(x-w/2, y-h/2, z) 72 | 73 | glTexCoord2f(1, 0) 74 | glVertex3f(x+w/2, y-h/2, z) 75 | 76 | glTexCoord2f(1, 1) 77 | glVertex3f(x+w/2, y+h/2, z) 78 | 79 | glTexCoord2f(0, 1) 80 | glVertex3f(x-w/2, y+h/2, z) 81 | 82 | glEnd() 83 | 84 | 85 | def run(): 86 | 87 | pygame.init() 88 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 89 | 90 | resize(*SCREEN_SIZE) 91 | init() 92 | 93 | # Upload the background and fugu texture 94 | background_tex = upload_texture('background.png') 95 | fugu_tex = upload_texture('fugu.png', True) 96 | 97 | while True: 98 | 99 | for event in pygame.event.get(): 100 | if event.type == QUIT: 101 | pygame.quit() 102 | quit() 103 | if event.type == KEYDOWN: 104 | if event.key == K_1: 105 | # Simple alpha blending 106 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 107 | glBlendEquation(GL_FUNC_ADD) 108 | elif event.key == K_2: 109 | # Additive alpha blending 110 | glBlendFunc(GL_SRC_ALPHA, GL_ONE) 111 | glBlendEquation(GL_FUNC_ADD) 112 | elif event.key == K_3: 113 | # Subtractive blending 114 | glBlendFunc(GL_SRC_ALPHA, GL_ONE) 115 | glBlendEquation(GL_FUNC_REVERSE_SUBTRACT) 116 | 117 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 118 | 119 | # Draw the background 120 | glBindTexture(GL_TEXTURE_2D, background_tex) 121 | 122 | glDisable(GL_BLEND) 123 | draw_quad(0, 0, -SCREEN_SIZE[1], 600, 600) 124 | glEnable(GL_BLEND) 125 | 126 | # Draw a texture at the mouse position 127 | glBindTexture(GL_TEXTURE_2D, fugu_tex) 128 | x, y = pygame.mouse.get_pos() 129 | x -= SCREEN_SIZE[0]/2 130 | y -= SCREEN_SIZE[1]/2 131 | draw_quad(x, -y, -SCREEN_SIZE[1], 256, 256) 132 | 133 | pygame.display.flip() 134 | 135 | # Free the textures we used 136 | glDeleteTextures(background_tex) 137 | glDeleteTextures(fugu_tex) 138 | 139 | if __name__ == "__main__": 140 | run() 141 | -------------------------------------------------------------------------------- /Chapter 12/bodytex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/bodytex.jpg -------------------------------------------------------------------------------- /Chapter 12/booktex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/booktex.png -------------------------------------------------------------------------------- /Chapter 12/fogdemo.py: -------------------------------------------------------------------------------- 1 | SCREEN_SIZE = (800, 600) 2 | 3 | from math import radians 4 | 5 | from OpenGL.GL import * 6 | from OpenGL.GLU import * 7 | 8 | import pygame 9 | from pygame.locals import * 10 | 11 | # Import the Model3D class 12 | import model3d 13 | 14 | def resize(width, height): 15 | 16 | glViewport(0, 0, width, height) 17 | glMatrixMode(GL_PROJECTION) 18 | glLoadIdentity() 19 | gluPerspective(60.0, float(width)/height, .1, 1000.) 20 | glMatrixMode(GL_MODELVIEW) 21 | glLoadIdentity() 22 | 23 | 24 | def init(): 25 | 26 | # Enable the GL features we will be using 27 | glEnable(GL_DEPTH_TEST) 28 | glEnable(GL_LIGHTING) 29 | glEnable(GL_COLOR_MATERIAL) 30 | glEnable(GL_TEXTURE_2D) 31 | glEnable(GL_CULL_FACE) 32 | 33 | glShadeModel(GL_SMOOTH) 34 | glClearColor(1.0, 1.0, 1.0, 0.0) # white 35 | 36 | # Set the material 37 | glMaterial(GL_FRONT, GL_AMBIENT, (0.0, 0.0, 0.0, 1.0)) 38 | glMaterial(GL_FRONT, GL_DIFFUSE, (0.2, 0.2, 0.2, 1.0)) 39 | glMaterial(GL_FRONT, GL_SPECULAR, (1.0, 1.0, 1.0, 1.0)) 40 | glMaterial(GL_FRONT, GL_SHININESS, 10.0) 41 | 42 | # Set light parameters 43 | glLight(GL_LIGHT0, GL_AMBIENT, (0.0, 0.0, 0.0, 1.0)) 44 | glLight(GL_LIGHT0, GL_DIFFUSE, (0.4, 0.4, 0.4, 1.0)) 45 | glLight(GL_LIGHT0, GL_SPECULAR, (1.0, 1.0, 1.0, 1.0)) 46 | 47 | # Enable light 1 and set position 48 | glEnable(GL_LIGHT0) 49 | glLight(GL_LIGHT0, GL_POSITION, (0, .5, 1, 0)) 50 | 51 | glEnable(GL_FOG) 52 | glFogfv(GL_FOG_COLOR, (1.0, 1.0, 1.0)) 53 | glFogi(GL_FOG_MODE, GL_LINEAR) 54 | glFogf(GL_FOG_START, 1.5) 55 | glFogf(GL_FOG_END, 3.5) 56 | 57 | 58 | #glFogi(GL_FOG_MODE, GL_LINEAR) 59 | #glFogf(GL_FOG_START, 5) 60 | #glFogf(GL_FOG_END, 6) 61 | 62 | glFogfv(GL_FOG_COLOR, (1.0, 0.7, 0.7)) 63 | glFogi(GL_FOG_MODE, GL_EXP2) 64 | glFogf(GL_FOG_DENSITY, 0.2) 65 | 66 | def run(): 67 | 68 | pygame.init() 69 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 70 | 71 | resize(*SCREEN_SIZE) 72 | init() 73 | 74 | clock = pygame.time.Clock() 75 | 76 | # Read the model 77 | tank_model = model3d.Model3D() 78 | tank_model.read_obj('mytank.obj') 79 | 80 | rotation = 0.0 81 | 82 | while True: 83 | 84 | for event in pygame.event.get(): 85 | if event.type == QUIT: 86 | pygame.quit() 87 | quit() 88 | 89 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 90 | 91 | time_passed = clock.tick() 92 | time_passed_seconds = time_passed / 1000.0 93 | 94 | 95 | 96 | glLoadIdentity() 97 | glRotatef(15, 1, 0, 0) 98 | 99 | tank_distance = pygame.mouse.get_pos()[1] / 50.0 100 | glTranslatef(0.0, -1.5, -tank_distance) 101 | 102 | #glTranslatef(0.0, -1.5, -3.5) 103 | 104 | rotation += time_passed_seconds * 45.0 105 | glRotatef(rotation, 0, 1, 0) 106 | 107 | tank_model.draw_quick() 108 | 109 | pygame.display.flip() 110 | 111 | 112 | if __name__ == "__main__": 113 | run() 114 | -------------------------------------------------------------------------------- /Chapter 12/fugu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/fugu.png -------------------------------------------------------------------------------- /Chapter 12/gameobjects/__pycache__/matrix44.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/gameobjects/__pycache__/matrix44.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 12/gameobjects/__pycache__/util.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/gameobjects/__pycache__/util.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 12/gameobjects/__pycache__/vector2.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/gameobjects/__pycache__/vector2.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 12/gameobjects/__pycache__/vector3.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/gameobjects/__pycache__/vector3.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 12/gameobjects/util.py: -------------------------------------------------------------------------------- 1 | from math import * 2 | 3 | def format_number(n, accuracy=6): 4 | """Formats a number in a friendly manner 5 | (removes trailing zeros and unneccesary point.""" 6 | 7 | fs = "%."+str(accuracy)+"f" 8 | str_n = fs%float(n) 9 | if '.' in str_n: 10 | str_n = str_n.rstrip('0').rstrip('.') 11 | if str_n == "-0": 12 | str_n = "0" 13 | #str_n = str_n.replace("-0", "0") 14 | return str_n 15 | 16 | 17 | def lerp(a, b, i): 18 | """Linear enterpolate from a to b.""" 19 | return a+(b-a)*i 20 | 21 | 22 | def range2d(range_x, range_y): 23 | 24 | """Creates a 2D range.""" 25 | 26 | range_x = list(range_x) 27 | return [ (x, y) for y in range_y for x in range_x ] 28 | 29 | 30 | def xrange2d(range_x, range_y): 31 | 32 | """Iterates over a 2D range.""" 33 | 34 | range_x = list(range_x) 35 | for y in range_y: 36 | for x in range_x: 37 | yield (x, y) 38 | 39 | 40 | def saturate(value, low, high): 41 | return min(max(value, low), high) 42 | 43 | 44 | def is_power_of_2(n): 45 | """Returns True if a value is a power of 2.""" 46 | return log(n, 2) % 1.0 == 0.0 47 | 48 | 49 | def next_power_of_2(n): 50 | """Returns the next power of 2 that is >= n""" 51 | return int(2 ** ceil(log(n, 2))) 52 | 53 | if __name__ == "__main__": 54 | 55 | print(list( xrange2d(range(3), range(3)) )) 56 | print(range2d(range(3), range(3))) 57 | print(is_power_of_2(7)) 58 | print(is_power_of_2(8)) 59 | print(is_power_of_2(9)) 60 | 61 | print(next_power_of_2(7)) 62 | -------------------------------------------------------------------------------- /Chapter 12/heavyfogtank.py.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | # Import the Model3D class 10 | import model3d 11 | 12 | SCREEN_SIZE = (800, 600) 13 | 14 | def resize(width, height): 15 | 16 | glViewport(0, 0, width, height) 17 | glMatrixMode(GL_PROJECTION) 18 | glLoadIdentity() 19 | gluPerspective(60.0, float(width)/height, .1, 1000.) 20 | glMatrixMode(GL_MODELVIEW) 21 | glLoadIdentity() 22 | 23 | 24 | def init(): 25 | glEnable(GL_FOG) 26 | glFogfv(GL_FOG_COLOR, (1.0, 1.0, 1.0)) 27 | glFogi(GL_FOG_MODE, GL_LINEAR) 28 | glFogf(GL_FOG_START, 1.5) 29 | glFogf(GL_FOG_END, 3.5) 30 | 31 | # Enable the GL features we will be using 32 | glEnable(GL_DEPTH_TEST) 33 | glEnable(GL_LIGHTING) 34 | glEnable(GL_COLOR_MATERIAL) 35 | glEnable(GL_TEXTURE_2D) 36 | glEnable(GL_CULL_FACE) 37 | 38 | glShadeModel(GL_SMOOTH) 39 | glClearColor(1.0, 1.0, 1.0, 0.0) # white 40 | 41 | # Set the material 42 | glMaterial(GL_FRONT, GL_AMBIENT, (0.0, 0.0, 0.0, 1.0)) 43 | glMaterial(GL_FRONT, GL_DIFFUSE, (0.2, 0.2, 0.2, 1.0)) 44 | glMaterial(GL_FRONT, GL_SPECULAR, (1.0, 1.0, 1.0, 1.0)) 45 | glMaterial(GL_FRONT, GL_SHININESS, 10.0) 46 | 47 | # Set light parameters 48 | glLight(GL_LIGHT0, GL_AMBIENT, (0.0, 0.0, 0.0, 1.0)) 49 | glLight(GL_LIGHT0, GL_DIFFUSE, (0.4, 0.4, 0.4, 1.0)) 50 | glLight(GL_LIGHT0, GL_SPECULAR, (1.0, 1.0, 1.0, 1.0)) 51 | 52 | # Enable light 1 and set position 53 | glEnable(GL_LIGHT0) 54 | glLight(GL_LIGHT0, GL_POSITION, (0, .5, 1, 0)) 55 | 56 | 57 | def run(): 58 | 59 | pygame.init() 60 | screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF) 61 | 62 | resize(*SCREEN_SIZE) 63 | init() 64 | 65 | clock = pygame.time.Clock() 66 | 67 | # Read the model 68 | tank_model = model3d.Model3D() 69 | tank_model.read_obj('mytank.obj') 70 | 71 | rotation = 0.0 72 | 73 | while True: 74 | 75 | for event in pygame.event.get(): 76 | if event.type == QUIT: 77 | pygame.quit() 78 | quit() 79 | 80 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 81 | 82 | time_passed = clock.tick() 83 | time_passed_seconds = time_passed / 1000.0 84 | 85 | glLoadIdentity() 86 | glRotatef(15, 1, 0, 0) 87 | glTranslatef(0.0, -1.5, -3.5) 88 | 89 | rotation += time_passed_seconds * 45.0 90 | glRotatef(rotation, 0, 1, 0) 91 | 92 | tank_model.draw_quick() 93 | 94 | pygame.display.flip() 95 | 96 | 97 | if __name__ == "__main__": 98 | run() 99 | -------------------------------------------------------------------------------- /Chapter 12/model3d.py: -------------------------------------------------------------------------------- 1 | 2 | from OpenGL.GL import * 3 | from OpenGL.GLU import * 4 | 5 | import pygame 6 | import os.path 7 | 8 | class Material(object): 9 | 10 | def __init__(self): 11 | 12 | self.name = "" 13 | self.texture_fname = None 14 | self.texture_id = None 15 | 16 | 17 | class FaceGroup(object): 18 | 19 | def __init__(self): 20 | 21 | self.tri_indices = [] 22 | self.material_name = "" 23 | 24 | 25 | class Model3D(object): 26 | 27 | def __init__(self): 28 | 29 | self.vertices = [] 30 | self.tex_coords = [] 31 | self.normals = [] 32 | self.materials = {} 33 | self.face_groups = [] 34 | self.display_list_id = None 35 | 36 | def __del__(self): 37 | 38 | #Called when the model is cleaned up by Python 39 | self.free_resources() 40 | 41 | def free_resources(self): 42 | 43 | # Delete the display list and textures 44 | if self.display_list_id is not None: 45 | glDeleteLists(self.display_list_id, 1) 46 | self.display_list_id = None 47 | 48 | # Delete any textures we used 49 | for material in self.materials.values(): 50 | if material.texture_id is not None: 51 | glDeleteTextures(material.texture_id) 52 | 53 | # Clear all the materials 54 | self.materials.clear() 55 | 56 | # Clear the geometry lists 57 | del self.vertices[:] 58 | del self.tex_coords[:] 59 | del self.normals[:] 60 | del self.face_groups[:] 61 | 62 | 63 | 64 | def read_obj(self, fname): 65 | 66 | current_face_group = None 67 | 68 | file_in = open(fname) 69 | 70 | for line in file_in: 71 | 72 | # Parse command and data from each line 73 | words = line.split() 74 | command = words[0] 75 | data = words[1:] 76 | 77 | if command == 'mtllib': # Material library 78 | 79 | model_path = os.path.split(fname)[0] 80 | mtllib_path = os.path.join( model_path, data[0] ) 81 | self.read_mtllib(mtllib_path) 82 | 83 | elif command == 'v': # Vertex 84 | x, y, z = data 85 | vertex = (float(x), float(y), float(z)) 86 | self.vertices.append(vertex) 87 | 88 | elif command == 'vt': # Texture coordinate 89 | 90 | s, t = data 91 | tex_coord = (float(s), float(t)) 92 | self.tex_coords.append(tex_coord) 93 | 94 | elif command == 'vn': # Normal 95 | 96 | x, y, z = data 97 | normal = (float(x), float(y), float(z)) 98 | self.normals.append(normal) 99 | 100 | elif command == 'usemtl' : # Use material 101 | 102 | current_face_group = FaceGroup() 103 | current_face_group.material_name = data[0] 104 | self.face_groups.append( current_face_group ) 105 | 106 | elif command == 'f': 107 | 108 | assert len(data) == 3, "Sorry, only triangles are supported" 109 | 110 | # Parse indices from triples 111 | for word in data: 112 | vi, ti, ni = word.split('/') 113 | indices = (int(vi) - 1, int(ti) - 1, int(ni) - 1) 114 | current_face_group.tri_indices.append(indices) 115 | 116 | 117 | for material in self.materials.values(): 118 | 119 | model_path = os.path.split(fname)[0] 120 | texture_path = os.path.join(model_path, material.texture_fname) 121 | texture_surface = pygame.image.load(texture_path) 122 | texture_data = pygame.image.tostring(texture_surface, 'RGB', True) 123 | 124 | material.texture_id = glGenTextures(1) 125 | glBindTexture(GL_TEXTURE_2D, material.texture_id) 126 | 127 | glTexParameteri( GL_TEXTURE_2D, 128 | GL_TEXTURE_MAG_FILTER, 129 | GL_LINEAR) 130 | glTexParameteri( GL_TEXTURE_2D, 131 | GL_TEXTURE_MIN_FILTER, 132 | GL_LINEAR_MIPMAP_LINEAR) 133 | 134 | glPixelStorei(GL_UNPACK_ALIGNMENT,1) 135 | width, height = texture_surface.get_rect().size 136 | gluBuild2DMipmaps( GL_TEXTURE_2D, 137 | 3, 138 | width, 139 | height, 140 | GL_RGB, 141 | GL_UNSIGNED_BYTE, 142 | texture_data) 143 | 144 | 145 | def read_mtllib(self, mtl_fname): 146 | 147 | file_mtllib = open(mtl_fname) 148 | for line in file_mtllib: 149 | 150 | words = line.split() 151 | command = words[0] 152 | data = words[1:] 153 | 154 | if command == 'newmtl': 155 | material = Material() 156 | material.name = data[0] 157 | self.materials[data[0]] = material 158 | 159 | elif command == 'map_Kd': 160 | material.texture_fname = data[0] 161 | 162 | 163 | def draw(self): 164 | 165 | vertices = self.vertices 166 | tex_coords = self.tex_coords 167 | normals = self.normals 168 | 169 | for face_group in self.face_groups: 170 | 171 | material = self.materials[face_group.material_name] 172 | glBindTexture(GL_TEXTURE_2D, material.texture_id) 173 | 174 | glBegin(GL_TRIANGLES) 175 | for vi, ti, ni in face_group.tri_indices: 176 | glTexCoord2fv( tex_coords[ti] ) 177 | glNormal3fv( normals[ni] ) 178 | glVertex3fv( vertices[vi] ) 179 | glEnd() 180 | 181 | 182 | def draw_quick(self): 183 | 184 | if self.display_list_id is None: 185 | self.display_list_id = glGenLists(1) 186 | glNewList(self.display_list_id, GL_COMPILE) 187 | self.draw() 188 | glEndList() 189 | 190 | glCallList(self.display_list_id) 191 | -------------------------------------------------------------------------------- /Chapter 12/mytank.mtl: -------------------------------------------------------------------------------- 1 | newmtl acmat_0 2 | Kd 1 1 1 3 | Ka 0.2 0.2 0.2 4 | Ks 0.2 0.2 0.2 5 | Ns 128 6 | Tr 0 7 | map_Kd booktex.png 8 | newmtl acmat_1 9 | Kd 1 1 1 10 | Ka 0.2 0.2 0.2 11 | Ks 0.2 0.2 0.2 12 | Ns 128 13 | Tr 0 14 | map_Kd bodytex.jpg 15 | newmtl acmat_2 16 | Kd 1 1 1 17 | Ka 0.2 0.2 0.2 18 | Ks 0.2 0.2 0.2 19 | Ns 128 20 | Tr 0 21 | map_Kd mytanktex.jpg 22 | -------------------------------------------------------------------------------- /Chapter 12/mytanktex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/mytanktex.jpg -------------------------------------------------------------------------------- /Chapter 12/skybox.py: -------------------------------------------------------------------------------- 1 | from math import radians 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | # Import the Model3D class 10 | import model3d 11 | 12 | SCREEN_SIZE = (800, 600) 13 | 14 | def resize(width, height): 15 | 16 | glViewport(0, 0, width, height) 17 | glMatrixMode(GL_PROJECTION) 18 | glLoadIdentity() 19 | gluPerspective(60.0, float(width)/height, .1, 1000.) 20 | glMatrixMode(GL_MODELVIEW) 21 | glLoadIdentity() 22 | 23 | 24 | def init(): 25 | 26 | # Enable the GL features we will be using 27 | glEnable(GL_DEPTH_TEST) 28 | glEnable(GL_LIGHTING) 29 | glEnable(GL_TEXTURE_2D) 30 | glShadeModel(GL_SMOOTH) 31 | 32 | # Enable light 1 and set position 33 | glEnable(GL_LIGHTING) 34 | glEnable(GL_LIGHT0) 35 | glLight(GL_LIGHT0, GL_POSITION, (0, .5, 1)) 36 | 37 | 38 | def run(): 39 | 40 | pygame.init() 41 | screen = pygame.display.set_mode(SCREEN_SIZE, FULLSCREEN|HWSURFACE|OPENGL|DOUBLEBUF) 42 | 43 | resize(*SCREEN_SIZE) 44 | init() 45 | 46 | # Read the skybox model 47 | sky_box = model3d.Model3D() 48 | sky_box.read_obj('tanksky/skybox.obj') 49 | 50 | # Set the wraping mode of all textures in the sky-box to GL_CLAMP_TO_EDGE 51 | for material in sky_box.materials.values(): 52 | 53 | glBindTexture(GL_TEXTURE_2D, material.texture_id) 54 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 55 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 56 | 57 | 58 | # Used to rotate the world 59 | mouse_x = 0.0 60 | mouse_y = 0.0 61 | 62 | #Don't display the mouse cursor 63 | pygame.mouse.set_visible(False) 64 | 65 | while True: 66 | 67 | for event in pygame.event.get(): 68 | if event.type == QUIT: 69 | pygame.quit() 70 | quit() 71 | if event.type == KEYDOWN: 72 | pygame.quit() 73 | quit() 74 | 75 | # We don't need to clear the color buffer (GL_COLOR_BUFFER_BIT) 76 | # because the skybox covers the entire screen 77 | glClear(GL_DEPTH_BUFFER_BIT) 78 | 79 | glLoadIdentity() 80 | 81 | mouse_rel_x, mouse_rel_y = pygame.mouse.get_rel() 82 | mouse_x += float(mouse_rel_x) / 5.0 83 | mouse_y += float(mouse_rel_y) / 5.0 84 | 85 | # Rotate around the x and y axes to create a mouse-look camera 86 | glRotatef(mouse_y, 1, 0, 0) 87 | glRotatef(mouse_x, 0, 1, 0) 88 | 89 | # Disable lighting and depth test 90 | glDisable(GL_LIGHTING) 91 | glDepthMask(False) 92 | 93 | # Draw the skybox 94 | sky_box.draw_quick() 95 | 96 | # Re-enable lighting and depth test before we redraw the world 97 | glEnable(GL_LIGHTING) 98 | glDepthMask(True) 99 | 100 | # Here is where we would draw the rest of the world in a game 101 | 102 | pygame.display.flip() 103 | 104 | if __name__ == "__main__": 105 | run() 106 | 107 | 108 | -------------------------------------------------------------------------------- /Chapter 12/tanksky/bk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/tanksky/bk.png -------------------------------------------------------------------------------- /Chapter 12/tanksky/dn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/tanksky/dn.png -------------------------------------------------------------------------------- /Chapter 12/tanksky/ft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/tanksky/ft.png -------------------------------------------------------------------------------- /Chapter 12/tanksky/lt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/tanksky/lt.png -------------------------------------------------------------------------------- /Chapter 12/tanksky/rt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/tanksky/rt.png -------------------------------------------------------------------------------- /Chapter 12/tanksky/skybox.mtl: -------------------------------------------------------------------------------- 1 | newmtl acmat_0 2 | Kd 1 1 1 3 | Ka 0.2 0.2 0.2 4 | Ks 0.2 0.2 0.2 5 | Ns 128 6 | Tr 0 7 | map_Kd rt.png 8 | newmtl acmat_1 9 | Kd 1 1 1 10 | Ka 0.2 0.2 0.2 11 | Ks 0.2 0.2 0.2 12 | Ns 128 13 | Tr 0 14 | map_Kd dn.png 15 | newmtl acmat_2 16 | Kd 1 1 1 17 | Ka 0.2 0.2 0.2 18 | Ks 0.2 0.2 0.2 19 | Ns 128 20 | Tr 0 21 | map_Kd up.png 22 | newmtl acmat_3 23 | Kd 1 1 1 24 | Ka 0.2 0.2 0.2 25 | Ks 0.2 0.2 0.2 26 | Ns 128 27 | Tr 0 28 | map_Kd ft.png 29 | newmtl acmat_4 30 | Kd 1 1 1 31 | Ka 0.2 0.2 0.2 32 | Ks 0.2 0.2 0.2 33 | Ns 128 34 | Tr 0 35 | map_Kd bk.png 36 | newmtl acmat_5 37 | Kd 1 1 1 38 | Ka 0.2 0.2 0.2 39 | Ks 0.2 0.2 0.2 40 | Ns 128 41 | Tr 0 42 | map_Kd lt.png 43 | -------------------------------------------------------------------------------- /Chapter 12/tanksky/skybox.obj: -------------------------------------------------------------------------------- 1 | mtllib skybox.mtl 2 | v -0.5 0.5 0.5 3 | v -0.5 -0.5 0.5 4 | v -0.5 -0.5 -0.5 5 | v -0.5 0.5 -0.5 6 | vn 1 0 0 7 | vn 1 0 0 8 | vn 1 0 0 9 | vn 1 0 0 10 | vn 1 0 0 11 | vn 1 0 0 12 | vt 1 0 13 | vt 0 0 14 | vt 1 1 15 | vt 0 1 16 | vt 1 1 17 | vt 0 0 18 | usemtl acmat_0 19 | f 2/1/1 3/2/2 1/3/3 20 | f 4/4/4 1/5/5 3/6/6 21 | v -0.5 -0.5 0.5 22 | v 0.5 -0.5 0.5 23 | v 0.5 -0.5 -0.5 24 | v -0.5 -0.5 -0.5 25 | vn 0 1 0 26 | vn 0 1 0 27 | vn 0 1 0 28 | vn 0 1 0 29 | vn 0 1 0 30 | vn 0 1 0 31 | vt 1.49012e-007 0 32 | vt 0 1 33 | vt 1 1.19209e-007 34 | vt 1 1 35 | vt 1 1.19209e-007 36 | vt 0 1 37 | usemtl acmat_1 38 | f 7/7/7 8/8/8 6/9/9 39 | f 5/10/10 6/11/11 8/12/12 40 | v -0.5 0.5 -0.5 41 | v 0.5 0.5 -0.5 42 | v 0.5 0.5 0.5 43 | v -0.5 0.5 0.5 44 | vn 0 -1 0 45 | vn 0 -1 0 46 | vn 0 -1 0 47 | vn 0 -1 0 48 | vn 0 -1 0 49 | vn 0 -1 0 50 | vt 5.96046e-008 1 51 | vt 1 0 52 | vt 0 5.96046e-008 53 | vt 1 0 54 | vt 5.96046e-008 1 55 | vt 1 1 56 | usemtl acmat_2 57 | f 10/13/13 12/14/14 9/15/15 58 | f 12/16/16 10/17/17 11/18/18 59 | v 0.5 0.5 0.5 60 | v 0.5 -0.5 0.5 61 | v -0.5 -0.5 0.5 62 | v -0.5 0.5 0.5 63 | vn 0 0 -1 64 | vn 0 0 -1 65 | vn 0 0 -1 66 | vn 0 0 -1 67 | vn 0 0 -1 68 | vn 0 0 -1 69 | vt 0 0 70 | vt 0 1 71 | vt 1 0 72 | vt 1 1 73 | vt 1 0 74 | vt 0 1 75 | usemtl acmat_3 76 | f 15/19/19 16/20/20 14/21/21 77 | f 13/22/22 14/23/23 16/24/24 78 | v -0.5 0.5 -0.5 79 | v -0.5 -0.5 -0.5 80 | v 0.5 -0.5 -0.5 81 | v 0.5 0.5 -0.5 82 | vn 0 0 1 83 | vn 0 0 1 84 | vn 0 0 1 85 | vn 0 0 1 86 | vn 0 0 1 87 | vn 0 0 1 88 | vt 0 0 89 | vt 1 1 90 | vt 1 0 91 | vt 1 1 92 | vt 0 0 93 | vt 0 1 94 | usemtl acmat_4 95 | f 19/25/25 17/26/26 18/27/27 96 | f 17/28/28 19/29/29 20/30/30 97 | v 0.5 0.5 -0.5 98 | v 0.5 -0.5 -0.5 99 | v 0.5 -0.5 0.5 100 | v 0.5 0.5 0.5 101 | vn -1 0 0 102 | vn -1 0 0 103 | vn -1 0 0 104 | vn -1 0 0 105 | vn -1 0 0 106 | vn -1 0 0 107 | vt 0 1 108 | vt 1 0 109 | vt 0 0 110 | vt 1 0 111 | vt 0 1 112 | vt 1 1 113 | usemtl acmat_5 114 | f 24/31/31 22/32/32 23/33/33 115 | f 22/34/34 24/35/35 21/36/36 116 | -------------------------------------------------------------------------------- /Chapter 12/tanksky/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 12/tanksky/up.png -------------------------------------------------------------------------------- /Chapter 2/tank.py: -------------------------------------------------------------------------------- 1 | class Tank(object): 2 | def __init__ (self, name): 3 | self.name = name 4 | self.alive = True 5 | self.ammo = 5 6 | self.armor = 60 7 | 8 | def __str__ (self): 9 | if self.alive: 10 | return "%s (%i armor, %i shells)" % (self.name, self.armor, self.ammo) 11 | else: 12 | return "%s (DEAD)" % self.name 13 | 14 | def fire_at(self, enemy): 15 | if self.ammo >= 1: 16 | self.ammo -= 1 17 | print(self.name, "fires on", enemy.name) 18 | enemy.hit() 19 | else: 20 | print(self.name, "has no shells!") 21 | 22 | def hit(self): 23 | self.armor -= 20 24 | print(self.name, "is hit!") 25 | if self.armor <= 0: 26 | self.explode() 27 | 28 | def explode(self): 29 | self.alive = False 30 | print(self.name, "explodes!") 31 | -------------------------------------------------------------------------------- /Chapter 2/tankgame.py: -------------------------------------------------------------------------------- 1 | from tank import Tank 2 | 3 | tanks = {"a":Tank("Alice"), "b":Tank("Bob"), "c":Tank("Carol") } 4 | alive_tanks = len(tanks) 5 | 6 | while alive_tanks > 1: 7 | 8 | 9 | for tank_name in sorted( tanks.keys() ): 10 | print(tank_name, tanks[tank_name]) 11 | 12 | first = input("Who fires? ").lower() 13 | second = input("Who at? " ).lower() 14 | 15 | try: 16 | first_tank = tanks[first] 17 | second_tank = tanks[second] 18 | except KeyError as name: 19 | print("No such tank!", name) 20 | continue 21 | 22 | if not first_tank.alive or not second_tank.alive: 23 | print("One of those tanks is dead!") 24 | continue 25 | 26 | 27 | print("*" * 30) 28 | 29 | first_tank.fire_at(second_tank) 30 | if not second_tank.alive: 31 | alive_tanks -= 1 32 | 33 | print("*" * 30) 34 | 35 | for tank in tanks.values(): 36 | if tank.alive: 37 | print(tank.name, "is the winner!") 38 | break 39 | -------------------------------------------------------------------------------- /Chapter 3/3-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | background_image_filename = 'sushiplate.jpg' 4 | mouse_image_filename = 'fugu.png' 5 | 6 | import pygame 7 | from pygame.locals import * 8 | from sys import exit 9 | 10 | pygame.init() 11 | 12 | screen = pygame.display.set_mode((640, 480), 0, 32) 13 | pygame.display.set_caption("Hello, World!") 14 | 15 | background = pygame.image.load(background_image_filename).convert() 16 | mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha() 17 | 18 | while True: 19 | 20 | for event in pygame.event.get(): 21 | if event.type == QUIT: 22 | pygame.quit() 23 | exit() 24 | 25 | screen.blit(background, (0,0)) 26 | 27 | x, y = pygame.mouse.get_pos() 28 | x-= mouse_cursor.get_width() / 2 29 | y-= mouse_cursor.get_height() / 2 30 | screen.blit(mouse_cursor, (x, y)) 31 | 32 | pygame.display.update() 33 | -------------------------------------------------------------------------------- /Chapter 3/3-2.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | pygame.init() 6 | SCREEN_SIZE = (800, 600) 7 | screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32) 8 | 9 | while True: 10 | 11 | for event in pygame.event.get(): 12 | if event.type == QUIT: 13 | pygame.quit() 14 | exit() 15 | print(event) 16 | 17 | pygame.display.update() 18 | -------------------------------------------------------------------------------- /Chapter 3/3-3.py: -------------------------------------------------------------------------------- 1 | background_image_filename = 'sushiplate.jpg' 2 | 3 | import pygame 4 | from pygame.locals import * 5 | from sys import exit 6 | 7 | pygame.init() 8 | screen = pygame.display.set_mode((640, 480), 0, 32) 9 | background = pygame.image.load(background_image_filename).convert() 10 | 11 | x, y = 0, 0 12 | move_x, move_y = 0, 0 13 | 14 | while True: 15 | 16 | for event in pygame.event.get(): 17 | if event.type == QUIT: 18 | pygame.quit() 19 | exit() 20 | 21 | if event.type == KEYDOWN: 22 | if event.key == K_LEFT: 23 | move_x = -1 24 | elif event.key == K_RIGHT: 25 | move_x = +1 26 | elif event.key == K_UP: 27 | move_y = -1 28 | elif event.key == K_DOWN: 29 | move_y = +1 30 | 31 | 32 | elif event.type == KEYUP: 33 | if event.key == K_LEFT: 34 | move_x = 0 35 | elif event.key == K_RIGHT: 36 | move_x = 0 37 | elif event.key == K_UP: 38 | move_y = 0 39 | elif event.key == K_DOWN: 40 | move_y = 0 41 | 42 | x+= move_x 43 | y+= move_y 44 | 45 | screen.fill((0, 0, 0)) 46 | screen.blit(background, (x, y)) 47 | 48 | pygame.display.update() 49 | -------------------------------------------------------------------------------- /Chapter 3/3-4.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | background_image_filename = 'sushiplate.jpg' 6 | 7 | pygame.init() 8 | screen = pygame.display.set_mode((640, 480), 0, 32) 9 | background = pygame.image.load(background_image_filename).convert() 10 | 11 | Fullscreen = False 12 | 13 | while True: 14 | 15 | for event in pygame.event.get(): 16 | if event.type == QUIT: 17 | pygame.quit() 18 | exit() 19 | if event.type == KEYDOWN: 20 | if event.key == K_f: 21 | Fullscreen = not Fullscreen 22 | if Fullscreen: 23 | screen = pygame.display.set_mode((640, 480), FULLSCREEN, 32) 24 | else: 25 | screen = pygame.display.set_mode((640, 480), 0, 32) 26 | 27 | screen.blit(background, (0,0)) 28 | pygame.display.update() 29 | -------------------------------------------------------------------------------- /Chapter 3/3-5.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | background_image_filename = 'sushiplate.jpg' 6 | 7 | SCREEN_SIZE = (640, 480) 8 | 9 | pygame.init() 10 | screen = pygame.display.set_mode(SCREEN_SIZE, RESIZABLE, 32) 11 | 12 | background = pygame.image.load(background_image_filename).convert() 13 | 14 | while True: 15 | 16 | event = pygame.event.wait() 17 | if event.type == QUIT: 18 | pygame.quit() 19 | exit() 20 | if event.type == VIDEORESIZE: 21 | SCREEN_SIZE = event.size 22 | screen = pygame.display.set_mode(SCREEN_SIZE, RESIZABLE, 32) 23 | pygame.display.set_caption("Window resized to "+str(event.size)) 24 | 25 | screen_width, screen_height = SCREEN_SIZE 26 | for y in range(0, screen_height, background.get_height()): 27 | for x in range(0, screen_width, background.get_width()): 28 | screen.blit(background, (x, y)) 29 | 30 | pygame.display.update() 31 | -------------------------------------------------------------------------------- /Chapter 3/3-6.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | my_name = "Harrison Kinsley" 4 | pygame.init() 5 | my_font = pygame.font.SysFont("arial", 64) 6 | name_surface = my_font.render(my_name, True, (0, 0, 0), (255, 255, 255)) 7 | pygame.image.save(name_surface, "name.png") 8 | -------------------------------------------------------------------------------- /Chapter 3/3-7.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | background_image_filename = 'sushiplate.jpg' 6 | SCREEN_SIZE = (640, 480) 7 | message=" This is a demonstration of the scrolly message script. " 8 | 9 | pygame.init() 10 | screen = pygame.display.set_mode(SCREEN_SIZE) 11 | 12 | font = pygame.font.SysFont("arial", 80); 13 | text_surface = font.render(message, True, (0, 0, 255)) 14 | 15 | x = 0 16 | y = ( SCREEN_SIZE[1] - text_surface.get_height() ) / 2 17 | 18 | background = pygame.image.load(background_image_filename).convert() 19 | 20 | while True: 21 | 22 | for event in pygame.event.get(): 23 | if event.type == QUIT: 24 | pygame.quit() 25 | exit() 26 | 27 | screen.blit(background, (0,0)) 28 | 29 | x-= 2 30 | if x < -text_surface.get_width(): 31 | x = 0 32 | 33 | screen.blit(text_surface, (x, y)) 34 | screen.blit(text_surface, (x+text_surface.get_width(), y)) 35 | pygame.display.update() 36 | -------------------------------------------------------------------------------- /Chapter 3/name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 3/name.png -------------------------------------------------------------------------------- /Chapter 4/4-1.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | pygame.init() 3 | 4 | screen = pygame.display.set_mode((640, 480)) 5 | 6 | all_colors = pygame.Surface((4096,4096), depth=24) 7 | 8 | for r in range(256): 9 | print(r+1, "out of 256") 10 | x = (r&15)*256 11 | y = (r>>4)*256 12 | for g in range(256): 13 | for b in range(256): 14 | all_colors.set_at((x+g, y+b), (r, g, b)) 15 | 16 | pygame.image.save(all_colors, "allcolors.bmp") 17 | pygame.quit() 18 | -------------------------------------------------------------------------------- /Chapter 4/4-10.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | pygame.init() 6 | screen = pygame.display.set_mode((640, 480), 0, 32) 7 | 8 | points = [] 9 | 10 | while True: 11 | 12 | for event in pygame.event.get(): 13 | if event.type == QUIT: 14 | pygame.quit() 15 | exit() 16 | if event.type == MOUSEBUTTONDOWN: 17 | points.append(event.pos) 18 | 19 | screen.fill((255,255,255)) 20 | 21 | if len(points) >= 3: 22 | pygame.draw.polygon(screen, (0,255,0), points) 23 | for point in points: 24 | pygame.draw.circle(screen, (0,0,255), point, 5) 25 | 26 | pygame.display.update() 27 | -------------------------------------------------------------------------------- /Chapter 4/4-11.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | from random import * 6 | 7 | pygame.init() 8 | screen = pygame.display.set_mode((640, 480), 0, 32) 9 | 10 | 11 | for _ in range(25): 12 | random_color = (randint(0,255), randint(0,255), randint(0,255)) 13 | random_pos = (randint(0,639), randint(0,479)) 14 | random_radius = randint(1,200) 15 | pygame.draw.circle(screen, random_color, random_pos, random_radius) 16 | 17 | pygame.display.update() 18 | 19 | 20 | while True: 21 | 22 | for event in pygame.event.get(): 23 | if event.type == QUIT: 24 | pygame.quit() 25 | exit() 26 | -------------------------------------------------------------------------------- /Chapter 4/4-12.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | from random import * 6 | 7 | pygame.init() 8 | screen = pygame.display.set_mode((640, 480), 0, 32) 9 | 10 | while True: 11 | 12 | for event in pygame.event.get(): 13 | if event.type == QUIT: 14 | pygame.quit() 15 | exit() 16 | 17 | x, y = pygame.mouse.get_pos() 18 | screen.fill((255,255,255)) 19 | pygame.draw.ellipse(screen, (0,255,0), (0,0,x,y)) 20 | 21 | pygame.display.update() 22 | -------------------------------------------------------------------------------- /Chapter 4/4-13.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | from random import * 6 | from math import pi 7 | 8 | pygame.init() 9 | screen = pygame.display.set_mode((640, 480), 0, 32) 10 | 11 | while True: 12 | 13 | for event in pygame.event.get(): 14 | if event.type == QUIT: 15 | pygame.quit() 16 | exit() 17 | 18 | x, y = pygame.mouse.get_pos() 19 | angle = (x/639.)*pi*2. 20 | screen.fill((255,255,255)) 21 | pygame.draw.arc(screen, (0,0,0), (0,0,639,479), 0, angle) 22 | 23 | pygame.display.update() 24 | -------------------------------------------------------------------------------- /Chapter 4/4-14.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | pygame.init() 6 | screen = pygame.display.set_mode((640, 480), 0, 32) 7 | 8 | while True: 9 | 10 | for event in pygame.event.get(): 11 | if event.type == QUIT: 12 | pygame.quit() 13 | exit() 14 | 15 | screen.fill((255, 255, 255)) 16 | 17 | mouse_pos = pygame.mouse.get_pos() 18 | 19 | for x in range(0,640,20): 20 | pygame.draw.line(screen, (0, 0, 0), (x, 0), mouse_pos) 21 | pygame.draw.line(screen, (0, 0, 0), (x, 479), mouse_pos) 22 | 23 | for y in range(0,480,20): 24 | pygame.draw.line(screen, (0, 0, 0), (0, y), mouse_pos) 25 | pygame.draw.line(screen, (0, 0, 0), (639, y), mouse_pos) 26 | 27 | pygame.display.update() 28 | -------------------------------------------------------------------------------- /Chapter 4/4-15.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | pygame.init() 6 | screen = pygame.display.set_mode((640, 480), 0, 32) 7 | 8 | points = [] 9 | 10 | while True: 11 | 12 | for event in pygame.event.get(): 13 | if event.type == QUIT: 14 | pygame.quit() 15 | exit() 16 | if event.type == MOUSEMOTION: 17 | points.append(event.pos) 18 | if len(points)>100: 19 | del points[0] 20 | 21 | screen.fill((255, 255, 255)) 22 | 23 | if len(points)>1: 24 | pygame.draw.lines(screen, (0,255,0), False, points, 2) 25 | 26 | pygame.display.update() 27 | -------------------------------------------------------------------------------- /Chapter 4/4-2.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | pygame.init() 6 | 7 | screen = pygame.display.set_mode((640, 480), 0, 32) 8 | 9 | # Creates images with smooth gradients 10 | def create_scales(height): 11 | red_scale_surface = pygame.surface.Surface((640, height)) 12 | green_scale_surface = pygame.surface.Surface((640, height)) 13 | blue_scale_surface = pygame.surface.Surface((640, height)) 14 | for x in range(640): 15 | c = int((x/639.)*255.) 16 | red = (c, 0, 0) 17 | green = (0, c, 0) 18 | blue = (0, 0, c) 19 | line_rect = Rect(x, 0, 1, height) 20 | pygame.draw.rect(red_scale_surface, red, line_rect) 21 | pygame.draw.rect(green_scale_surface, green, line_rect) 22 | pygame.draw.rect(blue_scale_surface, blue, line_rect) 23 | return red_scale_surface, green_scale_surface, blue_scale_surface 24 | 25 | red_scale, green_scale, blue_scale = create_scales(80) 26 | 27 | color = [127, 127, 127] 28 | 29 | while True: 30 | 31 | for event in pygame.event.get(): 32 | if event.type == QUIT: 33 | pygame.quit() 34 | exit() 35 | 36 | screen.fill((0, 0, 0)) 37 | 38 | # Draw the scales to the screen 39 | screen.blit(red_scale, (0, 00)) 40 | screen.blit(green_scale, (0, 80)) 41 | screen.blit(blue_scale, (0, 160)) 42 | 43 | x, y = pygame.mouse.get_pos() 44 | 45 | # If the mouse was pressed on one of the sliders, adjust the color component 46 | if pygame.mouse.get_pressed()[0]: 47 | for component in range(3): 48 | if y > component*80 and y < (component+1)*80: 49 | color[component] = int((x/639.)*255.) 50 | pygame.display.set_caption("PyGame Color Test - "+str(tuple(color))) 51 | 52 | # Draw a circle for each slider to represent the current setting 53 | for component in range(3): 54 | pos = ( int((color[component]/255.)*639), component*80+40 ) 55 | pygame.draw.circle(screen, (255, 255, 255), pos, 20) 56 | 57 | pygame.draw.rect(screen, tuple(color), (0, 240, 640, 240)) 58 | 59 | pygame.display.update() 60 | -------------------------------------------------------------------------------- /Chapter 4/4-3.py: -------------------------------------------------------------------------------- 1 | def scale_color(color, scale): 2 | red, green, blue = color 3 | red = int(red*scale) 4 | green = int(green*scale) 5 | blue = int(blue*scale) 6 | return red, green, blue 7 | 8 | fireball_orange = (221, 99, 20) 9 | print(fireball_orange) 10 | print(scale_color(fireball_orange, .5)) 11 | -------------------------------------------------------------------------------- /Chapter 4/4-4.py: -------------------------------------------------------------------------------- 1 | def saturate_color(color): 2 | red, green, blue = color 3 | red = min(red, 255) 4 | green = min(green, 255) 5 | blue = min(blue, 255) 6 | return red, green, blue 7 | -------------------------------------------------------------------------------- /Chapter 4/4-5.py: -------------------------------------------------------------------------------- 1 | def lerp(value1, value2, factor): 2 | return value1+(value2-value1)*factor 3 | 4 | print(lerp(100, 200, 0.)) 5 | print(lerp(100, 200, 1.)) 6 | print(lerp(100, 200, .5)) 7 | print(lerp(100, 200, .25)) 8 | -------------------------------------------------------------------------------- /Chapter 4/4-6.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | pygame.init() 6 | 7 | screen = pygame.display.set_mode((640, 480), 0, 32) 8 | 9 | color1 = (221, 99, 20) 10 | color2 = (96, 130, 51) 11 | factor = 0. 12 | 13 | def blend_color(color1, color2, blend_factor): 14 | red1, green1, blue1 = color1 15 | red2, green2, blue2 = color2 16 | red = red1+(red2-red1)*blend_factor 17 | green = green1+(green2-green1)*blend_factor 18 | blue = blue1+(blue2-blue1)*blend_factor 19 | return int(red), int(green), int(blue) 20 | 21 | while True: 22 | 23 | for event in pygame.event.get(): 24 | if event.type == QUIT: 25 | pygame.quit() 26 | exit() 27 | 28 | screen.fill((255, 255, 255)) 29 | 30 | tri = [ (0,120), (639,100), (639, 140) ] 31 | pygame.draw.polygon(screen, (0,255,0), tri) 32 | pygame.draw.circle(screen, (0,0,0), (int(factor*639.), 120), 10) 33 | 34 | x, y = pygame.mouse.get_pos() 35 | if pygame.mouse.get_pressed()[0]: 36 | factor = x / 639. 37 | pygame.display.set_caption("PyGame Color Blend Test - %.3f"%factor) 38 | 39 | color = blend_color(color1, color2, factor) 40 | pygame.draw.rect(screen, color, (0, 240, 640, 240)) 41 | 42 | pygame.display.update() 43 | -------------------------------------------------------------------------------- /Chapter 4/4-7.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | from random import randint 5 | 6 | pygame.init() 7 | screen = pygame.display.set_mode((640, 480), 0, 32) 8 | 9 | while True: 10 | 11 | for event in pygame.event.get(): 12 | if event.type == QUIT: 13 | pygame.quit() 14 | exit() 15 | 16 | rand_col = (randint(0, 255), randint(0, 255), randint(0, 255)) 17 | for _ in range(100): 18 | rand_pos = (randint(0, 639), randint(0, 479)) 19 | screen.set_at(rand_pos, rand_col) 20 | 21 | pygame.display.update() 22 | -------------------------------------------------------------------------------- /Chapter 4/4-8.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | from random import randint 5 | 6 | pygame.init() 7 | screen = pygame.display.set_mode((640, 480), 0, 32) 8 | 9 | while True: 10 | 11 | for event in pygame.event.get(): 12 | if event.type == QUIT: 13 | pygame.quit() 14 | exit() 15 | 16 | rand_col = (randint(0, 255), randint(0, 255), randint(0, 255)) 17 | screen.lock() 18 | for _ in range(100): 19 | rand_pos = (randint(0, 639), randint(0, 479)) 20 | screen.set_at(rand_pos, rand_col) 21 | screen.unlock() 22 | pygame.display.update() 23 | -------------------------------------------------------------------------------- /Chapter 4/4-9.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | from random import * 6 | 7 | pygame.init() 8 | screen = pygame.display.set_mode((640, 480), 0, 32) 9 | 10 | screen.lock() 11 | for count in range(10): 12 | random_color = (randint(0,255), randint(0,255), randint(0,255)) 13 | random_pos = (randint(0,639), randint(0,479)) 14 | random_size = (639-randint(random_pos[0],639), 479-randint(random_pos[1],479)) 15 | pygame.draw.rect(screen, random_color, Rect(random_pos, random_size)) 16 | 17 | screen.unlock() 18 | 19 | pygame.display.update() 20 | 21 | while True: 22 | for event in pygame.event.get(): 23 | if event.type == QUIT: 24 | pygame.quit() 25 | exit() 26 | -------------------------------------------------------------------------------- /Chapter 4/allcolors.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 4/allcolors.bmp -------------------------------------------------------------------------------- /Chapter 5/5-1.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | background_image_filename = 'sushiplate.jpg' 6 | sprite_image_filename = 'fugu.png' 7 | 8 | pygame.init() 9 | 10 | screen = pygame.display.set_mode((640, 480), 0, 32) 11 | 12 | background = pygame.image.load(background_image_filename).convert() 13 | sprite = pygame.image.load(sprite_image_filename) 14 | 15 | # The x coordinate of our sprite 16 | x = 0. 17 | 18 | while True: 19 | 20 | for event in pygame.event.get(): 21 | if event.type == QUIT: 22 | pygame.quit() 23 | exit() 24 | 25 | 26 | screen.blit(background, (0,0)) 27 | screen.blit(sprite, (x, 100)) 28 | x += 1 29 | 30 | # If the image goes off the end of the screen, move it back 31 | if x > 640: 32 | x -= 640 33 | 34 | pygame.display.update() 35 | -------------------------------------------------------------------------------- /Chapter 5/5-10.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Vector2: 4 | 5 | def __init__(self, x=0, y=0): 6 | self.x = x 7 | self.y = y 8 | 9 | def __str__(self): 10 | return "(%s, %s)"%(self.x, self.y) 11 | 12 | def from_points(P1, P2): 13 | return Vector2( P2[0] - P1[0], P2[1] - P1[1] ) 14 | 15 | def get_magnitude(self): 16 | return math.sqrt( self.x**2 + self.y**2 ) 17 | 18 | def normalize(self): 19 | magnitude = self.get_magnitude() 20 | self.x /= magnitude 21 | self.y /= magnitude 22 | 23 | # rhs stands for Right Hand Side 24 | def __add__(self, rhs): 25 | return Vector2(self.x + rhs.x, self.y + rhs.y) 26 | 27 | 28 | 29 | A = (10.0, 20.0) 30 | B = (30.0, 35.0) 31 | C = (15.0, 45.0) 32 | 33 | 34 | AB = Vector2.from_points(A, B) 35 | BC = Vector2.from_points(B, C) 36 | 37 | AC = Vector2.from_points(A, C) 38 | print("Vector AC is", AC) 39 | 40 | AC = AB + BC 41 | print("AB + BC is", AC) 42 | -------------------------------------------------------------------------------- /Chapter 5/5-11.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Vector2: 4 | 5 | def __init__(self, x=0, y=0): 6 | self.x = x 7 | self.y = y 8 | 9 | def __str__(self): 10 | return "(%s, %s)"%(self.x, self.y) 11 | 12 | def from_points(P1, P2): 13 | return Vector2( P2[0] - P1[0], P2[1] - P1[1] ) 14 | 15 | def get_magnitude(self): 16 | return math.sqrt( self.x**2 + self.y**2 ) 17 | 18 | def normalize(self): 19 | magnitude = self.get_magnitude() 20 | self.x /= magnitude 21 | self.y /= magnitude 22 | 23 | # rhs stands for Right Hand Side 24 | def __add__(self, rhs): 25 | return Vector2(self.x + rhs.x, self.y + rhs.y) 26 | 27 | def __sub__(self, rhs): 28 | return Vector2(self.x - rhs.x, self.y - rhs.y) 29 | 30 | 31 | 32 | A = (10.0, 20.0) 33 | B = (30.0, 35.0) 34 | C = (15.0, 45.0) 35 | 36 | 37 | AB = Vector2.from_points(A, B) 38 | BC = Vector2.from_points(B, C) 39 | 40 | AC = Vector2.from_points(A, C) 41 | print("Vector AC is", AC) 42 | 43 | AC = AB + BC 44 | print("AB + BC is", AC) 45 | -------------------------------------------------------------------------------- /Chapter 5/5-12.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Vector2: 4 | 5 | def __init__(self, x=0, y=0): 6 | self.x = x 7 | self.y = y 8 | 9 | def __str__(self): 10 | return "(%s, %s)"%(self.x, self.y) 11 | 12 | def from_points(P1, P2): 13 | return Vector2( P2[0] - P1[0], P2[1] - P1[1] ) 14 | 15 | def get_magnitude(self): 16 | return math.sqrt( self.x**2 + self.y**2 ) 17 | 18 | def normalize(self): 19 | magnitude = self.get_magnitude() 20 | self.x /= magnitude 21 | self.y /= magnitude 22 | 23 | # rhs stands for Right Hand Side 24 | def __add__(self, rhs): 25 | return Vector2(self.x + rhs.x, self.y + rhs.y) 26 | 27 | def __sub__(self, rhs): 28 | return Vector2(self.x - rhs.x, self.y - rhs.y) 29 | 30 | def __neg__(self): 31 | return Vector2(-self.x, -self.y) 32 | 33 | A = (10.0, 20.0) 34 | B = (30.0, 35.0) 35 | C = (15.0, 45.0) 36 | 37 | 38 | AB = Vector2.from_points(A, B) 39 | BC = Vector2.from_points(B, C) 40 | 41 | AC = Vector2.from_points(A, C) 42 | print("Vector AC is", AC) 43 | 44 | AC = AB + BC 45 | print("AB + BC is", AC) 46 | -------------------------------------------------------------------------------- /Chapter 5/5-13.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Vector2: 4 | 5 | def __init__(self, x=0, y=0): 6 | self.x = x 7 | self.y = y 8 | 9 | def __str__(self): 10 | return "(%s, %s)"%(self.x, self.y) 11 | 12 | def from_points(P1, P2): 13 | return Vector2( P2[0] - P1[0], P2[1] - P1[1] ) 14 | 15 | def get_magnitude(self): 16 | return math.sqrt( self.x**2 + self.y**2 ) 17 | 18 | def normalize(self): 19 | magnitude = self.get_magnitude() 20 | self.x /= magnitude 21 | self.y /= magnitude 22 | 23 | # rhs stands for Right Hand Side 24 | def __add__(self, rhs): 25 | return Vector2(self.x + rhs.x, self.y + rhs.y) 26 | 27 | def __sub__(self, rhs): 28 | return Vector2(self.x - rhs.x, self.y - rhs.y) 29 | 30 | def __neg__(self): 31 | return Vector2(-self.x, -self.y) 32 | 33 | def __mul__(self, scalar): 34 | return Vector2(self.x * scalar, self.y * scalar) 35 | 36 | def __truediv__(self, scalar): 37 | return Vector2(self.x / scalar, self.y / scalar) 38 | 39 | 40 | 41 | A = (10.0, 20.0) 42 | B = (30.0, 35.0) 43 | C = (15.0, 45.0) 44 | 45 | 46 | AB = Vector2.from_points(A, B) 47 | BC = Vector2.from_points(B, C) 48 | 49 | AC = Vector2.from_points(A, C) 50 | print("Vector AC is", AC) 51 | 52 | AC = AB + BC 53 | print("AB + BC is", AC) 54 | -------------------------------------------------------------------------------- /Chapter 5/5-14.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Vector2: 4 | 5 | def __init__(self, x=0, y=0): 6 | self.x = x 7 | self.y = y 8 | 9 | def __str__(self): 10 | return "(%s, %s)"%(self.x, self.y) 11 | 12 | def from_points(P1, P2): 13 | return Vector2( P2[0] - P1[0], P2[1] - P1[1] ) 14 | 15 | def get_magnitude(self): 16 | return math.sqrt( self.x**2 + self.y**2 ) 17 | 18 | def normalize(self): 19 | magnitude = self.get_magnitude() 20 | self.x /= magnitude 21 | self.y /= magnitude 22 | 23 | def __add__(self, rhs): 24 | return Vector2(self.x + rhs.x, self.y + rhs.y) 25 | 26 | def __sub__(self, rhs): 27 | return Vector2(self.x - rhs.x, self.y - rhs.y) 28 | 29 | def __neg__(self): 30 | return Vector2(-self.x, -self.y) 31 | 32 | def __mul__(self, scalar): 33 | return Vector2(self.x * scalar, self.y * scalar) 34 | 35 | def __truediv__(self, scalar): 36 | return Vector2(self.x / scalar, self.y / scalar) 37 | 38 | 39 | A = (10.0, 20.0) 40 | B = (30.0, 35.0) 41 | AB = Vector2.from_points(A, B) 42 | step = AB * .1 43 | position = Vector2(*A) 44 | for n in range(10): 45 | position += step 46 | print(position) 47 | 48 | -------------------------------------------------------------------------------- /Chapter 5/5-15.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Vector2: 4 | 5 | def __init__(self, x=0, y=0): 6 | self.x = x 7 | self.y = y 8 | 9 | if hasattr(x, "__getitem__"): 10 | x, y = x 11 | self._v = [float(x), float(y)] 12 | else: 13 | self._v = [float(x), float(y)] 14 | 15 | def __str__(self): 16 | return "(%s, %s)"%(self.x, self.y) 17 | 18 | def from_points(P1, P2): 19 | return Vector2( P2[0] - P1[0], P2[1] - P1[1] ) 20 | 21 | def get_magnitude(self): 22 | return math.sqrt( self.x**2 + self.y**2 ) 23 | 24 | def normalize(self): 25 | magnitude = self.get_magnitude() 26 | self.x /= magnitude 27 | self.y /= magnitude 28 | 29 | def __add__(self, rhs): 30 | return Vector2(self.x + rhs.x, self.y + rhs.y) 31 | 32 | def __sub__(self, rhs): 33 | return Vector2(self.x - rhs.x, self.y - rhs.y) 34 | 35 | def __neg__(self): 36 | return Vector2(-self.x, -self.y) 37 | 38 | def __mul__(self, scalar): 39 | return Vector2(self.x * scalar, self.y * scalar) 40 | 41 | def __truediv__(self, scalar): 42 | return Vector2(self.x / scalar, self.y / scalar) 43 | 44 | def __getitem__(self, index): 45 | return self._v[index] 46 | 47 | def __setitem__(self, index, value): 48 | self._v[index] = 1.0 * value 49 | 50 | 51 | if __name__ == "__main__": 52 | A = (10.0, 20.0) 53 | B = (30.0, 35.0) 54 | AB = Vector2.from_points(A, B) 55 | step = AB * .1 56 | position = Vector2(*A) 57 | for n in range(10): 58 | position += step 59 | print(position) 60 | 61 | -------------------------------------------------------------------------------- /Chapter 5/5-16.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | from gameobjects.vector2 import Vector2 5 | 6 | background_image_filename = 'sushiplate.jpg' 7 | sprite_image_filename = 'fugu.png' 8 | 9 | pygame.init() 10 | 11 | screen = pygame.display.set_mode((640, 480), 0, 32) 12 | 13 | background = pygame.image.load(background_image_filename).convert() 14 | sprite = pygame.image.load(sprite_image_filename).convert_alpha() 15 | 16 | clock = pygame.time.Clock() 17 | 18 | position = Vector2(100.0, 100.0) 19 | speed = 250 20 | heading = Vector2() 21 | 22 | while True: 23 | 24 | for event in pygame.event.get(): 25 | if event.type == QUIT: 26 | pygame.quit() 27 | exit() 28 | if event.type == MOUSEBUTTONDOWN: 29 | destination = Vector2(*event.pos) - (Vector2(*sprite.get_size())/2) 30 | heading = Vector2.from_points(position, destination) 31 | heading.normalize() 32 | 33 | screen.blit(background, (0,0)) 34 | screen.blit(sprite, (position.x, position.y)) 35 | 36 | time_passed = clock.tick() 37 | time_passed_seconds = time_passed / 1000.0 38 | 39 | distance_moved = time_passed_seconds * speed 40 | position += heading * distance_moved 41 | pygame.display.update() 42 | -------------------------------------------------------------------------------- /Chapter 5/5-17.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | from gameobjects.vector2 import Vector2 5 | 6 | background_image_filename = 'sushiplate.jpg' 7 | sprite_image_filename = 'fugu.png' 8 | 9 | pygame.init() 10 | 11 | screen = pygame.display.set_mode((640, 480), 0, 32) 12 | 13 | background = pygame.image.load(background_image_filename).convert() 14 | sprite = pygame.image.load(sprite_image_filename).convert_alpha() 15 | 16 | clock = pygame.time.Clock() 17 | 18 | position = Vector2(100.0, 100.0) 19 | speed = 250 20 | heading = Vector2() 21 | 22 | while True: 23 | 24 | for event in pygame.event.get(): 25 | if event.type == QUIT: 26 | pygame.quit() 27 | exit() 28 | if event.type == MOUSEBUTTONDOWN: 29 | destination_x = event.pos[0] - sprite.get_width()/2.0 30 | destination_y = event.pos[1] - sprite.get_height()/2.0 31 | destination = (destination_x, destination_y) 32 | 33 | 34 | heading = Vector2.from_points(position, destination) 35 | heading.normalize() 36 | 37 | screen.blit(background, (0,0)) 38 | screen.blit(sprite, (position.x, position.y)) 39 | 40 | time_passed = clock.tick() 41 | time_passed_seconds = time_passed / 1000.0 42 | 43 | distance_moved = time_passed_seconds * speed 44 | position += heading * distance_moved 45 | pygame.display.update() 46 | -------------------------------------------------------------------------------- /Chapter 5/5-2.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | background_image_filename = 'sushiplate.jpg' 6 | sprite_image_filename = 'fugu.png' 7 | 8 | pygame.init() 9 | 10 | screen = pygame.display.set_mode((640, 480), 0, 32) 11 | 12 | background = pygame.image.load(background_image_filename).convert() 13 | sprite = pygame.image.load(sprite_image_filename) 14 | 15 | # Our clock object 16 | clock = pygame.time.Clock() 17 | 18 | # X coordinate of our sprite 19 | x = 0 20 | # Speed in pixels per second 21 | speed = 250 22 | 23 | while True: 24 | 25 | for event in pygame.event.get(): 26 | if event.type == QUIT: 27 | pygame.quit() 28 | exit() 29 | 30 | 31 | screen.blit(background, (0,0)) 32 | screen.blit(sprite, (x, 100)) 33 | 34 | time_passed = clock.tick() 35 | time_passed_seconds = time_passed / 1000.0 36 | 37 | distance_moved = time_passed_seconds * speed 38 | x += distance_moved 39 | 40 | if x > 640: 41 | x -= 640 42 | 43 | pygame.display.update() 44 | -------------------------------------------------------------------------------- /Chapter 5/5-3.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | background_image_filename = 'sushiplate.jpg' 6 | sprite_image_filename = 'fugu.png' 7 | 8 | pygame.init() 9 | 10 | screen = pygame.display.set_mode((640, 480), 0, 32) 11 | 12 | background = pygame.image.load(background_image_filename).convert() 13 | sprite = pygame.image.load(sprite_image_filename) 14 | 15 | # Our clock object 16 | clock = pygame.time.Clock() 17 | 18 | x1 = 0 19 | x2 = 0 20 | # Speed in pixels per second 21 | speed = 250 22 | 23 | frame_no = 0 24 | 25 | 26 | while True: 27 | 28 | for event in pygame.event.get(): 29 | if event.type == QUIT: 30 | pygame.quit() 31 | exit() 32 | 33 | screen.blit(background, (0,0)) 34 | screen.blit(sprite, (x1, 50)) 35 | screen.blit(sprite, (x2, 250)) 36 | 37 | time_passed = clock.tick(30) 38 | time_passed_seconds = time_passed / 1000.0 39 | 40 | distance_moved = time_passed_seconds * speed 41 | x1 += distance_moved 42 | 43 | if (frame_no % 5) == 0: 44 | distance_moved = time_passed_seconds * speed 45 | x2 += distance_moved * 5 46 | 47 | # If the image goes off the end of the screen, move it back 48 | if x1 > 640: 49 | x1 -= 640 50 | if x2 > 640: 51 | x2 -= 640 52 | 53 | pygame.display.update() 54 | frame_no += 1 55 | -------------------------------------------------------------------------------- /Chapter 5/5-4.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | background_image_filename = 'sushiplate.jpg' 6 | sprite_image_filename = 'fugu.png' 7 | 8 | pygame.init() 9 | 10 | screen = pygame.display.set_mode((640, 480), 0, 32) 11 | 12 | background = pygame.image.load(background_image_filename).convert() 13 | sprite = pygame.image.load(sprite_image_filename).convert_alpha() 14 | 15 | clock = pygame.time.Clock() 16 | 17 | x, y = 100, 100 18 | speed_x, speed_y = 133, 170 19 | 20 | while True: 21 | 22 | for event in pygame.event.get(): 23 | if event.type == QUIT: 24 | pygame.quit() 25 | exit() 26 | 27 | screen.blit(background, (0,0)) 28 | screen.blit(sprite, (x, y)) 29 | 30 | time_passed = clock.tick(30) 31 | time_passed_seconds = time_passed / 1000.0 32 | 33 | x += speed_x * time_passed_seconds 34 | y += speed_y * time_passed_seconds 35 | 36 | 37 | # If the sprite goes off the edge of the screen, 38 | # make it move in the opposite direction 39 | if x > 640 - sprite.get_width(): 40 | speed_x = -speed_x 41 | x = 640 - sprite.get_width() 42 | elif x < 0: 43 | speed_x = -speed_x 44 | x = 0 45 | 46 | if y > 480 - sprite.get_height(): 47 | speed_y = -speed_y 48 | y = 480 - sprite.get_height() 49 | elif y < 0: 50 | speed_y = -speed_y 51 | y = 0 52 | 53 | pygame.display.update() 54 | -------------------------------------------------------------------------------- /Chapter 5/5-5.py: -------------------------------------------------------------------------------- 1 | class Vector2: 2 | 3 | def __init__(self, x=0, y=0): 4 | self.x = x 5 | self.y = y 6 | 7 | def __str__(self): 8 | return "(%s, %s)"%(self.x, self.y) 9 | -------------------------------------------------------------------------------- /Chapter 5/5-6.py: -------------------------------------------------------------------------------- 1 | class Vector2: 2 | 3 | def __init__(self, x=0, y=0): 4 | self.x = x 5 | self.y = y 6 | 7 | def __str__(self): 8 | return "(%s, %s)"%(self.x, self.y) 9 | 10 | def from_points(P1, P2): 11 | 12 | # ( P2[0] - P1[0], P2[1] - P1[1] ) is the same as: 13 | # numpy.array(P1) - numpy.array(P1) 14 | 15 | return Vector2(P2[0] - P1[0], P2[1] - P1[1]) 16 | -------------------------------------------------------------------------------- /Chapter 5/5-7.py: -------------------------------------------------------------------------------- 1 | class Vector2: 2 | 3 | def __init__(self, x=0, y=0): 4 | self.x = x 5 | self.y = y 6 | 7 | def __str__(self): 8 | return "(%s, %s)"%(self.x, self.y) 9 | 10 | def from_points(P1, P2): 11 | # ( P2[0] - P1[0], P2[1] - P1[1] ) is the same as: 12 | # numpy.array(P1) - numpy.array(P1) 13 | return Vector2(P2[0] - P1[0], P2[1] - P1[1]) 14 | 15 | 16 | A = (10.0, 20.0) 17 | B = (30.0, 35.0) 18 | AB = Vector2.from_points(A, B) 19 | print(AB) 20 | -------------------------------------------------------------------------------- /Chapter 5/5-8.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Vector2: 4 | 5 | def __init__(self, x=0, y=0): 6 | self.x = x 7 | self.y = y 8 | 9 | def __str__(self): 10 | return "(%s, %s)"%(self.x, self.y) 11 | 12 | def from_points(P1, P2): 13 | return Vector2( P2[0] - P1[0], P2[1] - P1[1] ) 14 | 15 | def get_magnitude(self): 16 | return math.sqrt( self.x**2 + self.y**2 ) 17 | 18 | 19 | 20 | A = (10.0, 20.0) 21 | B = (30.0, 35.0) 22 | AB = Vector2.from_points(A, B) 23 | print(AB) 24 | print(AB.get_magnitude()) 25 | -------------------------------------------------------------------------------- /Chapter 5/5-9.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Vector2: 4 | 5 | def __init__(self, x=0, y=0): 6 | self.x = x 7 | self.y = y 8 | 9 | def __str__(self): 10 | return "(%s, %s)"%(self.x, self.y) 11 | 12 | def from_points(P1, P2): 13 | return Vector2( P2[0] - P1[0], P2[1] - P1[1] ) 14 | 15 | def get_magnitude(self): 16 | return math.sqrt( self.x**2 + self.y**2 ) 17 | 18 | def normalize(self): 19 | magnitude = self.get_magnitude() 20 | self.x /= magnitude 21 | self.y /= magnitude 22 | 23 | 24 | 25 | A = (10.0, 20.0) 26 | B = (30.0, 35.0) 27 | AB = Vector2.from_points(A, B) 28 | print("Vector AB is", AB) 29 | print("Magnitude of Vector AB is", AB.get_magnitude()) 30 | AB.normalize() 31 | print("Vector AB normalized is", AB) 32 | -------------------------------------------------------------------------------- /Chapter 5/fugu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 5/fugu.png -------------------------------------------------------------------------------- /Chapter 5/gameobjects/vector2.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Vector2: 4 | 5 | def __init__(self, x=0, y=0): 6 | self.x = x 7 | self.y = y 8 | 9 | if hasattr(x, "__getitem__"): 10 | x, y = x 11 | self._v = [float(x), float(y)] 12 | else: 13 | self._v = [float(x), float(y)] 14 | 15 | def __str__(self): 16 | return "(%s, %s)"%(self.x, self.y) 17 | 18 | def from_points(P1, P2): 19 | return Vector2( P2[0] - P1[0], P2[1] - P1[1] ) 20 | 21 | def get_magnitude(self): 22 | return math.sqrt( self.x**2 + self.y**2 ) 23 | 24 | def normalize(self): 25 | magnitude = self.get_magnitude() 26 | self.x /= magnitude 27 | self.y /= magnitude 28 | 29 | def __add__(self, rhs): 30 | return Vector2(self.x + rhs.x, self.y + rhs.y) 31 | 32 | def __sub__(self, rhs): 33 | return Vector2(self.x - rhs.x, self.y - rhs.y) 34 | 35 | def __neg__(self): 36 | return Vector2(-self.x, -self.y) 37 | 38 | def __mul__(self, scalar): 39 | return Vector2(self.x * scalar, self.y * scalar) 40 | 41 | def __truediv__(self, scalar): 42 | return Vector2(self.x / scalar, self.y / scalar) 43 | 44 | def __getitem__(self, index): 45 | return self._v[index] 46 | 47 | def __setitem__(self, index, value): 48 | self._v[index] = 1.0 * value 49 | 50 | 51 | if __name__ == "__main__": 52 | A = (10.0, 20.0) 53 | B = (30.0, 35.0) 54 | AB = Vector2.from_points(A, B) 55 | step = AB * .1 56 | position = Vector2(*A) 57 | for n in range(10): 58 | position += step 59 | print(position) 60 | 61 | -------------------------------------------------------------------------------- /Chapter 5/sushiplate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 5/sushiplate.jpg -------------------------------------------------------------------------------- /Chapter 6/6-1.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | pygame.init() 6 | screen = pygame.display.set_mode((640, 480), 0, 32) 7 | 8 | font = pygame.font.SysFont("arial", 32); 9 | font_height = font.get_linesize() 10 | 11 | while True: 12 | 13 | for event in pygame.event.get(): 14 | if event.type == QUIT: 15 | pygame.quit() 16 | exit() 17 | 18 | screen.fill((255, 255, 255)) 19 | 20 | pressed_key_text = [] 21 | pressed_keys = pygame.key.get_pressed() 22 | y = font_height 23 | 24 | 25 | for key_constant, pressed in enumerate(pressed_keys): 26 | if pressed: 27 | key_name = pygame.key.name(key_constant) 28 | text_surface = font.render(key_name+" pressed", True, (0,0,0)) 29 | screen.blit(text_surface, (8, y)) 30 | y+= font_height 31 | 32 | pygame.display.update() 33 | -------------------------------------------------------------------------------- /Chapter 6/6-2.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | from gameobjects.vector2 import Vector2 5 | 6 | background_image_filename = 'sushiplate.jpg' 7 | sprite_image_filename = 'fugu.png' 8 | 9 | pygame.init() 10 | 11 | screen = pygame.display.set_mode((640, 480), 0, 32) 12 | background = pygame.image.load(background_image_filename).convert() 13 | sprite = pygame.image.load(sprite_image_filename).convert_alpha() 14 | 15 | clock = pygame.time.Clock() 16 | 17 | sprite_pos = Vector2(200, 150) 18 | sprite_speed = 300 19 | 20 | while True: 21 | 22 | for event in pygame.event.get(): 23 | if event.type == QUIT: 24 | pygame.quit() 25 | exit() 26 | 27 | pressed_keys = pygame.key.get_pressed() 28 | 29 | key_direction = Vector2(0, 0) 30 | 31 | if pressed_keys[K_LEFT]: 32 | key_direction.x = -1 33 | elif pressed_keys[K_RIGHT]: 34 | key_direction.x = +1 35 | if pressed_keys[K_UP]: 36 | key_direction.y = -1 37 | elif pressed_keys[K_DOWN]: 38 | key_direction.y = +1 39 | 40 | key_direction.normalize() 41 | 42 | screen.blit(background, (0,0)) 43 | screen.blit(sprite, (sprite_pos.x,sprite_pos.y)) 44 | 45 | time_passed = clock.tick(30) 46 | time_passed_seconds = time_passed / 1000.0 47 | 48 | sprite_pos += key_direction * sprite_speed * time_passed_seconds 49 | 50 | pygame.display.update() 51 | -------------------------------------------------------------------------------- /Chapter 6/6-3.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | from gameobjects.vector2 import Vector2 5 | from math import * 6 | 7 | background_image_filename = 'sushiplate.jpg' 8 | sprite_image_filename = 'fugu.png' 9 | 10 | pygame.init() 11 | 12 | screen = pygame.display.set_mode((640, 480), 0, 32) 13 | 14 | background = pygame.image.load(background_image_filename).convert() 15 | sprite = pygame.image.load(sprite_image_filename).convert_alpha() 16 | 17 | clock = pygame.time.Clock() 18 | 19 | sprite_pos = Vector2(200, 150) 20 | sprite_speed = 300 21 | sprite_rotation = 0 22 | sprite_rotation_speed = 360 # Degrees per second 23 | 24 | while True: 25 | 26 | for event in pygame.event.get(): 27 | if event.type == QUIT: 28 | pygame.quit() 29 | exit() 30 | pressed_keys = pygame.key.get_pressed() 31 | 32 | rotation_direction = 0. 33 | movement_direction = 0. 34 | 35 | if pressed_keys[K_LEFT]: 36 | rotation_direction = +1.0 37 | if pressed_keys[K_RIGHT]: 38 | rotation_direction = -1.0 39 | if pressed_keys[K_UP]: 40 | movement_direction = +1.0 41 | if pressed_keys[K_DOWN]: 42 | movement_direction = -1.0 43 | 44 | screen.blit(background, (0,0)) 45 | 46 | rotated_sprite = pygame.transform.rotate(sprite, sprite_rotation) 47 | w, h = rotated_sprite.get_size() 48 | sprite_draw_pos = Vector2(sprite_pos.x-w/2, sprite_pos.y-h/2) 49 | screen.blit(rotated_sprite, (sprite_draw_pos.x,sprite_draw_pos.y)) 50 | 51 | time_passed = clock.tick() 52 | time_passed_seconds = time_passed / 1000.0 53 | 54 | sprite_rotation += rotation_direction * sprite_rotation_speed * time_passed_seconds 55 | 56 | heading_x = sin(sprite_rotation*pi/180.0) 57 | heading_y = cos(sprite_rotation*pi/180.0) 58 | heading = Vector2(heading_x, heading_y) 59 | heading *= movement_direction 60 | 61 | sprite_pos+= heading * sprite_speed * time_passed_seconds 62 | 63 | pygame.display.update() 64 | -------------------------------------------------------------------------------- /Chapter 6/6-4.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | from gameobjects.vector2 import Vector2 5 | from math import * 6 | 7 | background_image_filename = 'sushiplate.jpg' 8 | sprite_image_filename = 'fugu.png' 9 | 10 | 11 | pygame.init() 12 | screen = pygame.display.set_mode((640, 480), 0, 32) 13 | 14 | background = pygame.image.load(background_image_filename).convert() 15 | sprite = pygame.image.load(sprite_image_filename).convert_alpha() 16 | 17 | clock = pygame.time.Clock() 18 | 19 | pygame.mouse.set_visible(False) 20 | pygame.event.set_grab(True) 21 | 22 | sprite_pos = Vector2(200, 150) 23 | sprite_speed = 300. 24 | sprite_rotation = 0. 25 | sprite_rotation_speed = 360. # Degrees per second 26 | 27 | while True: 28 | 29 | for event in pygame.event.get(): 30 | if event.type == QUIT: 31 | pygame.quit() 32 | exit() 33 | if event.type == KEYDOWN: 34 | if event.key == K_ESCAPE: 35 | pygame.quit() 36 | exit() 37 | 38 | 39 | pressed_keys = pygame.key.get_pressed() 40 | pressed_mouse = pygame.mouse.get_pressed() 41 | 42 | rotation_direction = 0. 43 | movement_direction = 0. 44 | 45 | rotation_direction = pygame.mouse.get_rel()[0] / 3. 46 | 47 | if pressed_keys[K_LEFT]: 48 | rotation_direction = +1. 49 | if pressed_keys[K_RIGHT]: 50 | rotation_direction = -1. 51 | if pressed_keys[K_UP] or pressed_mouse[0]: 52 | movement_direction = +1. 53 | if pressed_keys[K_DOWN] or pressed_mouse[2]: 54 | movement_direction = -1. 55 | screen.blit(background, (0,0)) 56 | 57 | rotated_sprite = pygame.transform.rotate(sprite, sprite_rotation) 58 | w, h = rotated_sprite.get_size() 59 | sprite_draw_pos = Vector2(sprite_pos.x-w/2, sprite_pos.y-h/2) 60 | screen.blit(rotated_sprite, (sprite_draw_pos.x, sprite_draw_pos.y)) 61 | 62 | time_passed = clock.tick() 63 | time_passed_seconds = time_passed / 1000.0 64 | 65 | sprite_rotation += rotation_direction * sprite_rotation_speed * time_passed_seconds 66 | 67 | heading_x = sin(sprite_rotation*pi/180.) 68 | heading_y = cos(sprite_rotation*pi/180.) 69 | heading = Vector2(heading_x, heading_y) 70 | heading *= movement_direction 71 | 72 | sprite_pos+= heading * sprite_speed * time_passed_seconds 73 | 74 | pygame.display.update() 75 | -------------------------------------------------------------------------------- /Chapter 6/6-5.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | pygame.init() 6 | 7 | SCREEN_SIZE = (640, 480) 8 | screen = pygame.display.set_mode( SCREEN_SIZE, 0, 32) 9 | 10 | font = pygame.font.SysFont("arial", 16); 11 | font_height = font.get_linesize() 12 | event_text = [] 13 | 14 | joysticks = [] 15 | for joystick_no in range(pygame.joystick.get_count()): 16 | stick = pygame.joystick.Joystick(joystick_no) 17 | stick.init() 18 | joysticks.append(stick) 19 | 20 | 21 | while True: 22 | 23 | event = pygame.event.wait() 24 | if event.type in (JOYAXISMOTION, 25 | JOYBALLMOTION, 26 | JOYHATMOTION, 27 | JOYBUTTONUP, 28 | JOYBUTTONDOWN): 29 | event_text.append(str(event)) 30 | 31 | 32 | event_text = event_text[int(-SCREEN_SIZE[1]/font_height):] 33 | 34 | if event.type == QUIT: 35 | pygame.quit() 36 | exit() 37 | 38 | screen.fill((255, 255, 255)) 39 | 40 | y = SCREEN_SIZE[1]-font_height 41 | for text in reversed(event_text): 42 | screen.blit( font.render(text, True, (0, 0, 0)), (0, y) ) 43 | y-=font_height 44 | 45 | pygame.display.update() 46 | -------------------------------------------------------------------------------- /Chapter 6/6-6.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | from gameobjects.vector2 import Vector2 5 | 6 | picture_file = 'map.png' 7 | 8 | pygame.init() 9 | screen = pygame.display.set_mode((640, 480), 0, 32) 10 | 11 | picture = pygame.image.load(picture_file).convert() 12 | picture_pos = Vector2(0, 0) 13 | scroll_speed = 1000. 14 | 15 | clock = pygame.time.Clock() 16 | 17 | joystick = None 18 | if pygame.joystick.get_count() > 0: 19 | joystick = pygame.joystick.Joystick(0) 20 | joystick.init() 21 | 22 | if joystick is None: 23 | print("Sorry, you need a joystick for this!") 24 | pygame.quit() 25 | exit() 26 | 27 | 28 | while True: 29 | 30 | for event in pygame.event.get(): 31 | if event.type == QUIT: 32 | pygame.quit() 33 | exit() 34 | 35 | scroll_direction = Vector2(*joystick.get_hat(0)) 36 | scroll_direction.normalize() 37 | 38 | screen.fill((255, 255, 255)) 39 | screen.blit(picture, (-picture_pos.x, picture_pos.y)) 40 | 41 | time_passed = clock.tick() 42 | time_passed_seconds = time_passed / 1000.0 43 | 44 | picture_pos += scroll_direction * scroll_speed * time_passed_seconds 45 | 46 | pygame.display.update() 47 | -------------------------------------------------------------------------------- /Chapter 6/6-7.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | from gameobjects.vector2 import Vector2 5 | 6 | picture_file = 'map.jpg' 7 | 8 | pygame.init() 9 | screen = pygame.display.set_mode((640, 480), 0, 32) 10 | 11 | picture = pygame.image.load(picture_file).convert() 12 | picture_pos = Vector2(0, 0) 13 | scroll_speed = 1000. 14 | 15 | clock = pygame.time.Clock() 16 | 17 | joystick = None 18 | if pygame.joystick.get_count() > 0: 19 | joystick = pygame.joystick.Joystick(0) 20 | joystick.init() 21 | 22 | if joystick is None: 23 | print("Sorry, you need a joystick for this!") 24 | pygame.quit() 25 | exit() 26 | 27 | while True: 28 | 29 | for event in pygame.event.get(): 30 | if event.type == QUIT: 31 | pygame.quit() 32 | exit() 33 | 34 | 35 | scroll_direction = Vector2(0, 0) 36 | if joystick.get_numhats() > 0: 37 | scroll_direction = Vector2(*joystick.get_hat(0)) 38 | scroll_direction.normalize() 39 | 40 | analog_scroll = Vector2(0, 0) 41 | if joystick.get_numaxes() >= 2: 42 | axis_x = joystick.get_axis(0) 43 | axis_y = joystick.get_axis(1) 44 | analog_scroll = Vector2(axis_x, -axis_y) 45 | 46 | screen.fill((255, 255, 255)) 47 | screen.blit(picture, (-picture_pos.x, picture_pos.y)) 48 | 49 | time_passed = clock.tick() 50 | time_passed_seconds = time_passed / 1000.0 51 | 52 | picture_pos += scroll_direction * scroll_speed * time_passed_seconds 53 | picture_pos += analog_scroll * scroll_speed * time_passed_seconds 54 | 55 | pygame.display.update() 56 | -------------------------------------------------------------------------------- /Chapter 6/6-8.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from sys import exit 4 | 5 | pygame.init() 6 | screen = pygame.display.set_mode((640, 480), 0, 32) 7 | 8 | # Get a list of joystick objects 9 | joysticks = [] 10 | for joystick_no in range(pygame.joystick.get_count()): 11 | stick = pygame.joystick.Joystick(joystick_no) 12 | stick.init() 13 | joysticks.append(stick) 14 | 15 | if not joysticks: 16 | print("Sorry! No joystick(s) to test.") 17 | pygame.quit() 18 | exit() 19 | 20 | active_joystick = 0 21 | 22 | 23 | pygame.display.set_caption(joysticks[0].get_name()) 24 | 25 | def draw_axis(surface, x, y, axis_x, axis_y, size): 26 | 27 | line_col = (128, 128, 128) 28 | num_lines = 40 29 | step = size / float(num_lines) 30 | for n in range(num_lines): 31 | line_col = [(192, 192, 192), (220, 220, 220)][n&1] 32 | pygame.draw.line(surface, line_col, (x+n*step, y), (x+n*step, y+size)) 33 | pygame.draw.line(surface, line_col, (x, y+n*step), (x+size, y+n*step)) 34 | 35 | pygame.draw.line(surface, (0, 0, 0), (x, y+size/2), (x+size, y+size/2)) 36 | pygame.draw.line(surface, (0, 0, 0), (x+size/2, y), (x+size/2, y+size)) 37 | 38 | draw_x = int(x + (axis_x * size + size) / 2.) 39 | draw_y = int(y + (axis_y * size + size) / 2.) 40 | draw_pos = (draw_x, draw_y) 41 | center_pos = (x+size/2, y+size/2) 42 | pygame.draw.line(surface, (0, 0, 0), center_pos, draw_pos, 5) 43 | pygame.draw.circle(surface, (0, 0, 255), draw_pos, 10) 44 | 45 | 46 | def draw_dpad(surface, x, y, axis_x, axis_y): 47 | 48 | col = (255, 0, 0) 49 | if axis_x == -1: 50 | pygame.draw.circle(surface, col, (x-20, y), 10) 51 | elif axis_x == +1: 52 | pygame.draw.circle(surface, col, (x+20, y), 10) 53 | 54 | if axis_y == -1: 55 | pygame.draw.circle(surface, col, (x, y+20), 10) 56 | elif axis_y == +1: 57 | pygame.draw.circle(surface, col, (x, y-20), 10) 58 | 59 | while True: 60 | 61 | joystick = joysticks[active_joystick] 62 | 63 | for event in pygame.event.get(): 64 | if event.type == QUIT: 65 | pygame.quit() 66 | exit() 67 | 68 | if event.type == KEYDOWN: 69 | if event.key >= K_0 and event.key <= K_1: 70 | num = event.key - K_0 71 | 72 | if num < len(joysticks): 73 | active_joystick = num 74 | name = joysticks[active_joystick].get_name() 75 | pygame.display.set_caption(name) 76 | 77 | # Get a list of all the axis 78 | axes = [] 79 | for axis_no in range(joystick.get_numaxes()): 80 | axes.append( joystick.get_axis(axis_no) ) 81 | 82 | axis_size = min(256, 640 / (joystick.get_numaxes()/2)) 83 | 84 | pygame.draw.rect(screen, (255, 255,255), (0, 0, 640, 480)) 85 | 86 | # Draw all the axes (analog sticks) 87 | x = 0 88 | for axis_no in range(0, len(axes), 2): 89 | axis_x = axes[axis_no] 90 | if axis_no+1 < len(axes): 91 | axis_y = axes[axis_no+1] 92 | else: 93 | axis_y = 0. 94 | draw_axis(screen, x, 0, axis_x, axis_y, axis_size) 95 | x += axis_size 96 | 97 | 98 | # Draw all the hats (d-pads) 99 | x, y = 50, 300 100 | for hat_no in range(joystick.get_numhats()): 101 | axis_x, axis_y = joystick.get_hat(hat_no) 102 | draw_dpad(screen, x, y, axis_x, axis_y) 103 | x+= 100 104 | 105 | 106 | #Draw all the buttons x, y = 0.0, 390.0 107 | button_width = 640 / joystick.get_numbuttons() 108 | for button_no in range(joystick.get_numbuttons()): 109 | if joystick.get_button(button_no): 110 | pygame.draw.circle(screen, (0, 255, 0), (int(x), int(y)), 20) 111 | x += button_width 112 | 113 | pygame.display.update() 114 | -------------------------------------------------------------------------------- /Chapter 6/fugu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 6/fugu.png -------------------------------------------------------------------------------- /Chapter 6/gameobjects/__pycache__/vector2.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 6/gameobjects/__pycache__/vector2.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 6/gameobjects/vector2.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Vector2: 4 | 5 | def __init__(self, x=0, y=0): 6 | self.x = x 7 | self.y = y 8 | 9 | if hasattr(x, "__getitem__"): 10 | x, y = x 11 | self._v = [float(x), float(y)] 12 | else: 13 | self._v = [float(x), float(y)] 14 | 15 | def __str__(self): 16 | return "(%s, %s)"%(self.x, self.y) 17 | 18 | def from_points(P1, P2): 19 | return Vector2( P2[0] - P1[0], P2[1] - P1[1] ) 20 | 21 | def get_magnitude(self): 22 | return math.sqrt( self.x**2 + self.y**2 ) 23 | 24 | def normalize(self): 25 | magnitude = self.get_magnitude() 26 | 27 | try: 28 | self.x /= magnitude 29 | self.y /= magnitude 30 | except ZeroDivisionError: 31 | self.x = 0 32 | self.y = 0 33 | 34 | def __add__(self, rhs): 35 | return Vector2(self.x + rhs.x, self.y + rhs.y) 36 | 37 | def __sub__(self, rhs): 38 | return Vector2(self.x - rhs.x, self.y - rhs.y) 39 | 40 | def __neg__(self): 41 | return Vector2(-self.x, -self.y) 42 | 43 | def __mul__(self, scalar): 44 | return Vector2(self.x * scalar, self.y * scalar) 45 | 46 | def __truediv__(self, scalar): 47 | return Vector2(self.x / scalar, self.y / scalar) 48 | 49 | def __getitem__(self, index): 50 | return self._v[index] 51 | 52 | def __setitem__(self, index, value): 53 | self._v[index] = 1.0 * value 54 | 55 | 56 | if __name__ == "__main__": 57 | A = (10.0, 20.0) 58 | B = (30.0, 35.0) 59 | AB = Vector2.from_points(A, B) 60 | step = AB * .1 61 | position = Vector2(*A) 62 | for n in range(10): 63 | position += step 64 | print(position) 65 | 66 | -------------------------------------------------------------------------------- /Chapter 6/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 6/map.png -------------------------------------------------------------------------------- /Chapter 6/sushiplate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 6/sushiplate.jpg -------------------------------------------------------------------------------- /Chapter 7/7-1.py: -------------------------------------------------------------------------------- 1 | self.move_forward() 2 | if self.hit_wall(): 3 | self.change_direction() 4 | -------------------------------------------------------------------------------- /Chapter 7/7-2.py: -------------------------------------------------------------------------------- 1 | if self.state == "exploring": 2 | self.random_heading() 3 | if self.can_see(player): 4 | self.state = "seeking" 5 | 6 | elif self.state == "seeking": 7 | self.head_towards("player") 8 | if self.in_range_of(player): 9 | self.fire_at(player) 10 | if not self.can_see(player): 11 | self.state = "exploring" 12 | -------------------------------------------------------------------------------- /Chapter 7/7-3.py: -------------------------------------------------------------------------------- 1 | class GameEntity(object): 2 | 3 | def __init__(self, world, name, image): 4 | 5 | self.world = world 6 | self.name = name 7 | self.image = image 8 | self.location = Vector2(0, 0) 9 | self.destination = Vector2(0, 0) 10 | self.speed = 0. 11 | 12 | self.brain = StateMachine() 13 | 14 | self.id = 0 15 | 16 | def render(self, surface): 17 | 18 | x, y = self.location 19 | w, h = self.image.get_size() 20 | surface.blit(self.image, (x-w/2, y-h/2)) 21 | 22 | def process(self, time_passed): 23 | 24 | self.brain.think() 25 | 26 | if self.speed > 0 and self.location != self.destination: 27 | 28 | vec_to_destination = self.destination - self.location 29 | distance_to_destination = vec_to_destination.get_length() 30 | heading = vec_to_destination.get_normalized() 31 | travel_distance = min(distance_to_destination, time_passed * self.speed) 32 | self.location += travel_distance * heading 33 | -------------------------------------------------------------------------------- /Chapter 7/7-4.py: -------------------------------------------------------------------------------- 1 | class World(object): 2 | 3 | def __init__(self): 4 | 5 | self.entities = {} # Store all the entities 6 | self.entity_id = 0 # Last entity id assigned 7 | # Draw the nest (a circle) on the background 8 | self.background = pygame.surface.Surface(SCREEN_SIZE).convert() 9 | self.background.fill((255, 255, 255)) 10 | pygame.draw.circle(self.background, (200, 255, 200), NEST_POSITION, int(NEST_SIZE)) 11 | 12 | def add_entity(self, entity): 13 | 14 | # Stores the entity then advances the current id 15 | self.entities[self.entity_id] = entity 16 | entity.id = self.entity_id 17 | self.entity_id += 1 18 | 19 | def remove_entity(self, entity): 20 | 21 | del self.entities[entity.id] 22 | 23 | def get(self, entity_id): 24 | 25 | # Find the entity, given its id (or None if it is not found) 26 | if entity_id in self.entities: 27 | return self.entities[entity_id] 28 | else: 29 | return None 30 | 31 | def process(self, time_passed): 32 | 33 | # Process every entity in the world 34 | time_passed_seconds = time_passed / 1000.0 35 | for entity in self.entities.itervalues(): 36 | entity.process(time_passed_seconds) 37 | 38 | def render(self, surface): 39 | 40 | # Draw the background and all the entities 41 | surface.blit(self.background, (0, 0)) 42 | for entity in self.entities.values(): 43 | entity.render(surface) 44 | 45 | def get_close_entity(self, name, location, e_range=100): 46 | 47 | # Find an entity within range of a location 48 | location = Vector2(*location) 49 | 50 | for entity in self.entities.values(): 51 | if entity.name == name: 52 | distance = location.get_distance_to(entity.location) 53 | if distance < e_range: 54 | return entity 55 | return None 56 | -------------------------------------------------------------------------------- /Chapter 7/7-5.py: -------------------------------------------------------------------------------- 1 | class Ant(GameEntity): 2 | 3 | def __init__(self, world, image): 4 | 5 | # Call the base class constructor 6 | GameEntity.__init__(self, world, "ant", image) 7 | 8 | # Create instances of each of the states 9 | exploring_state = AntStateExploring(self) 10 | seeking_state = AntStateSeeking(self) 11 | delivering_state = AntStateDelivering(self) 12 | hunting_state = AntStateHunting(self) 13 | 14 | # Add the states to the state machine (self.brain) 15 | self.brain.add_state(exploring_state) 16 | self.brain.add_state(seeking_state) 17 | self.brain.add_state(delivering_state) 18 | self.brain.add_state(hunting_state) 19 | 20 | self.carry_image = None 21 | 22 | def carry(self, image): 23 | 24 | self.carry_image = image 25 | 26 | def drop(self, surface): 27 | 28 | # Blit the 'carry' image to the background and reset it 29 | if self.carry_image: 30 | x, y = self.location 31 | w, h = self.carry_image.get_size() 32 | surface.blit(self.carry_image, (x-w, y-h/2)) 33 | self.carry_image = None 34 | 35 | def render(self, surface): 36 | 37 | # Call the render function of the base class 38 | GameEntity.render(self, surface) 39 | 40 | # Extra code to render the 'carry' image 41 | if self.carry_image: 42 | x, y = self.location 43 | w, h = self.carry_image.get_size() 44 | surface.blit(self.carry_image, (x-w, y-h/2)) 45 | 46 | -------------------------------------------------------------------------------- /Chapter 7/7-6.py: -------------------------------------------------------------------------------- 1 | class State(object): 2 | 3 | def __init__(self, name): 4 | self.name = name 5 | 6 | def do_actions(self): 7 | pass 8 | 9 | def check_conditions(self): 10 | pass 11 | 12 | def entry_actions(self): 13 | pass 14 | 15 | def exit_actions(self): 16 | pass 17 | -------------------------------------------------------------------------------- /Chapter 7/7-7.py: -------------------------------------------------------------------------------- 1 | class StateMachine(object): 2 | 3 | def __init__(self): 4 | 5 | self.states = {} # Stores the states 6 | self.active_state = None # The currently active state 7 | 8 | def add_state(self, state): 9 | 10 | # Add a state to the internal dictionary 11 | self.states[state.name] = state 12 | 13 | def think(self): 14 | 15 | # Only continue if there is an active state 16 | if self.active_state is None: 17 | return 18 | 19 | # Perform the actions of the active state, and check conditions 20 | self.active_state.do_actions() 21 | 22 | new_state_name = self.active_state.check_conditions() 23 | if new_state_name is not None: 24 | self.set_state(new_state_name) 25 | 26 | def set_state(self, new_state_name): 27 | 28 | # Change states and perform any exit / entry actions 29 | if self.active_state is not None: 30 | self.active_state.exit_actions() 31 | 32 | self.active_state = self.states[new_state_name] 33 | self.active_state.entry_actions() 34 | -------------------------------------------------------------------------------- /Chapter 7/7-8.py: -------------------------------------------------------------------------------- 1 | class AntStateExploring(State): 2 | 3 | def __init__(self, ant): 4 | 5 | # Call the base class constructor to initialize the State 6 | State.__init__(self, "exploring") 7 | # Set the ant that this State will manipulate 8 | self.ant = ant 9 | 10 | def random_destination(self): 11 | 12 | # Select a point in the screen 13 | w, h = SCREEN_SIZE 14 | self.ant.destination = Vector2(randint(0, w), randint(0, h)) 15 | 16 | def do_actions(self): 17 | 18 | # Change direction, 1 in 20 calls 19 | if randint(1, 20) == 1: 20 | self.random_destination() 21 | 22 | def check_conditions(self): 23 | 24 | # If there is a nearby leaf, switch to seeking state 25 | leaf = self.ant.world.get_close_entity("leaf", self.ant.location) 26 | if leaf is not None: 27 | self.ant.leaf_id = leaf.id 28 | return "seeking" 29 | # If there is a nearby spider, switch to hunting state 30 | spider = self.ant.world.get_close_entity("spider", NEST_POSITION, NEST_SIZE) 31 | if spider is not None: 32 | if self.ant.location.get_distance_to(spider.location) < 100.: 33 | self.ant.spider_id = spider.id 34 | return "hunting" 35 | 36 | return None 37 | 38 | def entry_actions(self): 39 | 40 | # Start with random speed and heading 41 | self.ant.speed = 120. + randint(-30, 30) 42 | self.random_destination() 43 | -------------------------------------------------------------------------------- /Chapter 7/ant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 7/ant.png -------------------------------------------------------------------------------- /Chapter 7/gameobjects/util.py: -------------------------------------------------------------------------------- 1 | from math import * 2 | 3 | def format_number(n, accuracy=6): 4 | """Formats a number in a friendly manner 5 | (removes trailing zeros and unneccesary point.""" 6 | 7 | fs = "%."+str(accuracy)+"f" 8 | str_n = fs%float(n) 9 | if '.' in str_n: 10 | str_n = str_n.rstrip('0').rstrip('.') 11 | if str_n == "-0": 12 | str_n = "0" 13 | #str_n = str_n.replace("-0", "0") 14 | return str_n 15 | 16 | 17 | def lerp(a, b, i): 18 | """Linear enterpolate from a to b.""" 19 | return a+(b-a)*i 20 | 21 | 22 | def range2d(range_x, range_y): 23 | 24 | """Creates a 2D range.""" 25 | 26 | range_x = list(range_x) 27 | return [ (x, y) for y in range_y for x in range_x ] 28 | 29 | 30 | def xrange2d(range_x, range_y): 31 | 32 | """Iterates over a 2D range.""" 33 | 34 | range_x = list(range_x) 35 | for y in range_y: 36 | for x in range_x: 37 | yield (x, y) 38 | 39 | 40 | def saturate(value, low, high): 41 | return min(max(value, low), high) 42 | 43 | 44 | def is_power_of_2(n): 45 | """Returns True if a value is a power of 2.""" 46 | return log(n, 2) % 1.0 == 0.0 47 | 48 | 49 | def next_power_of_2(n): 50 | """Returns the next power of 2 that is >= n""" 51 | return int(2 ** ceil(log(n, 2))) 52 | 53 | if __name__ == "__main__": 54 | 55 | print(list( xrange2d(xrange(3), xrange(3)) )) 56 | print(range2d(xrange(3), xrange(3))) 57 | print(is_power_of_2(7)) 58 | print(is_power_of_2(8)) 59 | print(is_power_of_2(9)) 60 | 61 | print(next_power_of_2(7)) 62 | -------------------------------------------------------------------------------- /Chapter 7/gameobjects/vector2-init.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Vector2: 4 | 5 | def __init__(self, x=0, y=0): 6 | self.x = x 7 | self.y = y 8 | 9 | if hasattr(x, "__getitem__"): 10 | x, y = x 11 | self._v = [float(x), float(y)] 12 | else: 13 | self._v = [float(x), float(y)] 14 | 15 | def __str__(self): 16 | return "(%s, %s)"%(self.x, self.y) 17 | 18 | def from_points(P1, P2): 19 | return Vector2( P2[0] - P1[0], P2[1] - P1[1] ) 20 | 21 | def get_magnitude(self): 22 | return math.sqrt( self.x**2 + self.y**2 ) 23 | 24 | def normalize(self): 25 | magnitude = self.get_magnitude() 26 | 27 | try: 28 | self.x /= magnitude 29 | self.y /= magnitude 30 | except ZeroDivisionError: 31 | self.x = 0 32 | self.y = 0 33 | 34 | def __add__(self, rhs): 35 | return Vector2(self.x + rhs.x, self.y + rhs.y) 36 | 37 | def __sub__(self, rhs): 38 | return Vector2(self.x - rhs.x, self.y - rhs.y) 39 | 40 | def __neg__(self): 41 | return Vector2(-self.x, -self.y) 42 | 43 | def __mul__(self, scalar): 44 | return Vector2(self.x * scalar, self.y * scalar) 45 | 46 | def __truediv__(self, scalar): 47 | return Vector2(self.x / scalar, self.y / scalar) 48 | 49 | def __getitem__(self, index): 50 | return self._v[index] 51 | 52 | def __setitem__(self, index, value): 53 | self._v[index] = 1.0 * value 54 | 55 | 56 | if __name__ == "__main__": 57 | A = (10.0, 20.0) 58 | B = (30.0, 35.0) 59 | AB = Vector2.from_points(A, B) 60 | step = AB * .1 61 | position = Vector2(*A) 62 | for n in range(10): 63 | position += step 64 | print(position) 65 | 66 | -------------------------------------------------------------------------------- /Chapter 7/leaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 7/leaf.png -------------------------------------------------------------------------------- /Chapter 7/spider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 7/spider.png -------------------------------------------------------------------------------- /Chapter 8/8-1.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from random import randint 4 | 5 | class Star(object): 6 | 7 | def __init__(self, x, y, speed): 8 | 9 | self.x = x 10 | self.y = y 11 | self.speed = speed 12 | 13 | def run(): 14 | 15 | pygame.init() 16 | screen = pygame.display.set_mode((640, 480), FULLSCREEN) 17 | 18 | stars = [] 19 | 20 | # Add a few stars for the first frame 21 | for n in range(200): 22 | 23 | x = float(randint(0, 639)) 24 | y = float(randint(0, 479)) 25 | speed = float(randint(10, 300)) 26 | stars.append( Star(x, y, speed) ) 27 | 28 | clock = pygame.time.Clock() 29 | 30 | white = (255, 255, 255) 31 | 32 | while True: 33 | 34 | for event in pygame.event.get(): 35 | if event.type == QUIT: 36 | pygame.quit() 37 | quit() 38 | if event.type == KEYDOWN: 39 | pygame.quit() 40 | quit() 41 | 42 | # Add a new star 43 | y = float(randint(0, 479)) 44 | speed = float(randint(10, 300)) 45 | star = Star(640, y, speed) 46 | 47 | stars.append(star) 48 | time_passed = clock.tick() 49 | time_passed_seconds = time_passed / 1000. 50 | 51 | screen.fill((0, 0, 0)) 52 | 53 | # Draw the stars 54 | for star in stars: 55 | 56 | new_x = star.x - time_passed_seconds * star.speed 57 | pygame.draw.aaline(screen, white, (new_x, star.y), (star.x+1., star.y)) 58 | star.x = new_x 59 | 60 | def on_screen(star): 61 | return star.x > 0 62 | 63 | # Remove stars that are no longer visible 64 | stars = list(filter(on_screen, stars)) 65 | 66 | pygame.display.update() 67 | 68 | 69 | if __name__ == "__main__": 70 | run() 71 | -------------------------------------------------------------------------------- /Chapter 8/8-2.py: -------------------------------------------------------------------------------- 1 | from math import sqrt 2 | 3 | class Vector3(object): 4 | 5 | def init (self, x, y, z): 6 | 7 | self.x = x 8 | self.y = y 9 | self.z = z 10 | 11 | def add (self, x, y, z): 12 | 13 | return Vector3(self.x + x, self.y + y, self.z + z) 14 | 15 | def get_magnitude(self): 16 | 17 | return sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2) 18 | -------------------------------------------------------------------------------- /Chapter 8/8-3.py: -------------------------------------------------------------------------------- 1 | from gameobjects.vector3 import * 2 | 3 | A = Vector3(6, 8, 12) 4 | B = Vector3(10, 16, 12) 5 | 6 | print("A is", A) 7 | print("B is", B) 8 | print("Magnitude of A is", A.get_magnitude()) 9 | print("A+B is", A+B) 10 | print("A-B is", A-B) 11 | 12 | 13 | print("A normalized is", A.get_normalized()) 14 | print("A*2 is", A * 2) 15 | -------------------------------------------------------------------------------- /Chapter 8/8-4.py: -------------------------------------------------------------------------------- 1 | from gameobjects.vector3 import * 2 | 3 | A = (-6, 2, 2) 4 | B = (7, 5, 10) 5 | plasma_speed = 100 # meters per second 6 | 7 | AB = Vector3.from_points(A, B) 8 | print("Vector to droid is", AB) 9 | 10 | distance_to_target = AB.get_magnitude() 11 | print("Distance to droid is", distance_to_target, "meters") 12 | 13 | plasma_heading = AB.get_normalized() 14 | print("Heading is", plasma_heading) 15 | -------------------------------------------------------------------------------- /Chapter 8/8-5.py: -------------------------------------------------------------------------------- 1 | def parallel_project(vector3): 2 | return (vector3.x, vector3.y) 3 | -------------------------------------------------------------------------------- /Chapter 8/8-6.py: -------------------------------------------------------------------------------- 1 | def perspective_project(vector3, d): 2 | x, y, z = vector3 3 | return (x * d/z, -y * d/z) 4 | -------------------------------------------------------------------------------- /Chapter 8/8-7.py: -------------------------------------------------------------------------------- 1 | from math import tan 2 | 3 | def calculate_viewing_distance(fov, screen_width): 4 | 5 | d = (screen_width/2.0) / tan(fov/2.0) 6 | return d 7 | -------------------------------------------------------------------------------- /Chapter 8/8-8.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from gameobjects.vector3 import Vector3 4 | from math import * 5 | from random import randint 6 | 7 | SCREEN_SIZE = (640, 480) 8 | CUBE_SIZE = 300 9 | 10 | def calculate_viewing_distance(fov, screen_width): 11 | 12 | d = (screen_width/2.0) / tan(fov/2.0) 13 | return d 14 | 15 | 16 | def run(): 17 | 18 | pygame.init() 19 | screen = pygame.display.set_mode(SCREEN_SIZE, 0) 20 | 21 | default_font = pygame.font.get_default_font() 22 | font = pygame.font.SysFont(default_font, 24) 23 | 24 | ball = pygame.image.load("ball.png").convert_alpha() 25 | 26 | # The 3D points 27 | points = [] 28 | 29 | fov = 90. # Field of view 30 | viewing_distance = calculate_viewing_distance(radians(fov), SCREEN_SIZE[0]) 31 | 32 | # Create a list of points along the edge of a cube 33 | for x in range(0, CUBE_SIZE+1, 20): 34 | edge_x = x == 0 or x == CUBE_SIZE 35 | 36 | for y in range(0, CUBE_SIZE+1, 20): 37 | edge_y = y == 0 or y == CUBE_SIZE 38 | 39 | for z in range(0, CUBE_SIZE+1, 20): 40 | edge_z = z == 0 or z == CUBE_SIZE 41 | 42 | if sum((edge_x, edge_y, edge_z)) >= 2: 43 | 44 | point_x = float(x) - CUBE_SIZE/2 45 | point_y = float(y) - CUBE_SIZE/2 46 | point_z = float(z) - CUBE_SIZE/2 47 | 48 | points.append(Vector3(point_x, point_y, point_z)) 49 | 50 | # Sort points in z order 51 | def point_z(point): 52 | return point.z 53 | points.sort(key=point_z, reverse=True) 54 | 55 | center_x, center_y = SCREEN_SIZE 56 | center_x /= 2 57 | center_y /= 2 58 | 59 | ball_w, ball_h = ball.get_size() 60 | ball_center_x = ball_w / 2 61 | ball_center_y = ball_h / 2 62 | 63 | camera_position = Vector3(0.0, 0.0, -700.) 64 | camera_speed = Vector3(300.0, 300.0, 300.0) 65 | 66 | clock = pygame.time.Clock() 67 | 68 | while True: 69 | 70 | for event in pygame.event.get(): 71 | if event.type == QUIT: 72 | pygame.quit() 73 | quit() 74 | 75 | screen.fill((0, 0, 0)) 76 | 77 | pressed_keys = pygame.key.get_pressed() 78 | 79 | time_passed = clock.tick() 80 | time_passed_seconds = time_passed / 1000. 81 | 82 | direction = Vector3() 83 | if pressed_keys[K_LEFT]: 84 | direction.x = -1.0 85 | elif pressed_keys[K_RIGHT]: 86 | direction.x = +1.0 87 | 88 | if pressed_keys[K_UP]: 89 | direction.y = +1.0 90 | elif pressed_keys[K_DOWN]: 91 | direction.y = -1.0 92 | 93 | if pressed_keys[K_q]: 94 | direction.z = +1.0 95 | elif pressed_keys[K_a]: 96 | direction.z = -1.0 97 | 98 | if pressed_keys[K_w]: 99 | fov = min(179., fov+1.) 100 | w = SCREEN_SIZE[0] 101 | viewing_distance = calculate_viewing_distance(radians(fov), w) 102 | elif pressed_keys[K_s]: 103 | fov = max(1., fov-1.) 104 | w = SCREEN_SIZE[0] 105 | viewing_distance = calculate_viewing_distance(radians(fov), w) 106 | 107 | camera_position += direction * camera_speed * time_passed_seconds 108 | 109 | # Draw the 3D points 110 | for point in points: 111 | 112 | x, y, z = point - camera_position 113 | 114 | if z > 0: 115 | x = x * viewing_distance / z 116 | y = -y * viewing_distance / z 117 | x += center_x 118 | y += center_y 119 | screen.blit(ball, (x-ball_center_x, y-ball_center_y)) 120 | 121 | # Draw the field of view diagram 122 | diagram_width = SCREEN_SIZE[0] / 4 123 | col = (50, 255, 50) 124 | diagram_points = [] 125 | diagram_points.append( (diagram_width/2, 100+viewing_distance/4) ) 126 | diagram_points.append( (0, 100) ) 127 | diagram_points.append( (diagram_width, 100) ) 128 | diagram_points.append( (diagram_width/2, 100+viewing_distance/4) ) 129 | diagram_points.append( (diagram_width/2, 100) ) 130 | pygame.draw.lines(screen, col, False, diagram_points, 2) 131 | 132 | # Draw the text 133 | white = (255, 255, 255) 134 | cam_text = font.render("camera = "+str(camera_position), True, white) 135 | screen.blit(cam_text, (5, 5)) 136 | fov_text = font.render("field of view = %i"%int(fov), True, white) 137 | screen.blit(fov_text, (5, 35)) 138 | txt = "viewing distance = %.3f"%viewing_distance 139 | d_text = font.render(txt, True, white) 140 | screen.blit(d_text, (5, 65)) 141 | 142 | pygame.display.update() 143 | 144 | 145 | if __name__ == "__main__": 146 | run() 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /Chapter 8/ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 8/ball.png -------------------------------------------------------------------------------- /Chapter 8/gameobjects/__pycache__/util.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 8/gameobjects/__pycache__/util.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 8/gameobjects/__pycache__/vector2.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 8/gameobjects/__pycache__/vector2.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 8/gameobjects/__pycache__/vector3.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 8/gameobjects/__pycache__/vector3.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 8/gameobjects/util.py: -------------------------------------------------------------------------------- 1 | from math import * 2 | 3 | def format_number(n, accuracy=6): 4 | """Formats a number in a friendly manner 5 | (removes trailing zeros and unneccesary point.""" 6 | 7 | fs = "%."+str(accuracy)+"f" 8 | str_n = fs%float(n) 9 | if '.' in str_n: 10 | str_n = str_n.rstrip('0').rstrip('.') 11 | if str_n == "-0": 12 | str_n = "0" 13 | #str_n = str_n.replace("-0", "0") 14 | return str_n 15 | 16 | 17 | def lerp(a, b, i): 18 | """Linear enterpolate from a to b.""" 19 | return a+(b-a)*i 20 | 21 | 22 | def range2d(range_x, range_y): 23 | 24 | """Creates a 2D range.""" 25 | 26 | range_x = list(range_x) 27 | return [ (x, y) for y in range_y for x in range_x ] 28 | 29 | 30 | def xrange2d(range_x, range_y): 31 | 32 | """Iterates over a 2D range.""" 33 | 34 | range_x = list(range_x) 35 | for y in range_y: 36 | for x in range_x: 37 | yield (x, y) 38 | 39 | 40 | def saturate(value, low, high): 41 | return min(max(value, low), high) 42 | 43 | 44 | def is_power_of_2(n): 45 | """Returns True if a value is a power of 2.""" 46 | return log(n, 2) % 1.0 == 0.0 47 | 48 | 49 | def next_power_of_2(n): 50 | """Returns the next power of 2 that is >= n""" 51 | return int(2 ** ceil(log(n, 2))) 52 | 53 | if __name__ == "__main__": 54 | 55 | print(list( xrange2d(range(3), range(3)) )) 56 | print(range2d(range(3), range(3))) 57 | print(is_power_of_2(7)) 58 | print(is_power_of_2(8)) 59 | print(is_power_of_2(9)) 60 | 61 | print(next_power_of_2(7)) 62 | -------------------------------------------------------------------------------- /Chapter 9/9-1.py: -------------------------------------------------------------------------------- 1 | from gameobjects.matrix44 import * 2 | 3 | identity = Matrix44() 4 | print(identity) 5 | p1 = (1.0, 2.0, 3.0) 6 | identity.transform(p1) 7 | 8 | assert identity.get_column(3) == (0, 0, 0, 1), "Something is wrong with this matrix!" 9 | -------------------------------------------------------------------------------- /Chapter 9/9-2.py: -------------------------------------------------------------------------------- 1 | #pseudocode 2 | tank_heading = Vector3(tank_matrix.forward) 3 | tank_matrix.translation += tank_heading * tank_speed * time_passed 4 | -------------------------------------------------------------------------------- /Chapter 9/9-3.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from gameobjects.vector3 import Vector3 4 | from gameobjects.matrix44 import Matrix44 as Matrix 5 | from math import * 6 | from random import randint 7 | 8 | SCREEN_SIZE = (640, 480) 9 | CUBE_SIZE = 300 10 | 11 | def calculate_viewing_distance(fov, screen_width): 12 | 13 | d = (screen_width/2.0) / tan(fov/2.0) 14 | return d 15 | 16 | 17 | def run(): 18 | 19 | pygame.init() 20 | screen = pygame.display.set_mode(SCREEN_SIZE, 0) 21 | 22 | font = pygame.font.SysFont("courier new", 16, True) 23 | 24 | ball = pygame.image.load("ball.png").convert_alpha() 25 | 26 | points = [] 27 | 28 | fov = 75. # Field of view 29 | viewing_distance = calculate_viewing_distance(radians(fov), SCREEN_SIZE[0]) 30 | 31 | # Create a list of points along the edge of a cube 32 | for x in range(0, CUBE_SIZE+1, 10): 33 | edge_x = x == 0 or x == CUBE_SIZE 34 | 35 | for y in range(0, CUBE_SIZE+1, 10): 36 | edge_y = y == 0 or y == CUBE_SIZE 37 | 38 | for z in range(0, CUBE_SIZE+1, 10): 39 | edge_z = z == 0 or z == CUBE_SIZE 40 | 41 | if sum((edge_x, edge_y, edge_z)) >= 2: 42 | 43 | point_x = float(x) - CUBE_SIZE/2 44 | point_y = float(y) - CUBE_SIZE/2 45 | point_z = float(z) - CUBE_SIZE/2 46 | 47 | points.append(Vector3(point_x, point_y, point_z)) 48 | 49 | def point_z(point): 50 | return point[2] 51 | 52 | center_x, center_y = SCREEN_SIZE 53 | center_x /= 2 54 | center_y /= 2 55 | 56 | ball_w, ball_h = ball.get_size() 57 | ball_center_x = ball_w / 2 58 | ball_center_y = ball_h / 2 59 | 60 | camera_position = Vector3(0.0, 0.0, 600.) 61 | 62 | rotation = Vector3() 63 | rotation_speed = Vector3(radians(20), radians(20), radians(20)) 64 | 65 | clock = pygame.time.Clock() 66 | 67 | # Some colors for drawing 68 | red = (255, 0, 0) 69 | green = (0, 255, 0) 70 | blue = (0, 0, 255) 71 | white = (255, 255, 255) 72 | 73 | # Labels for the axes 74 | x_surface = font.render("X", True, white) 75 | y_surface = font.render("Y", True, white) 76 | z_surface = font.render("Z", True, white) 77 | 78 | while True: 79 | 80 | for event in pygame.event.get(): 81 | if event.type == QUIT: 82 | pygame.quit() 83 | quit() 84 | 85 | screen.fill((0, 0, 0)) 86 | 87 | time_passed = clock.tick() 88 | time_passed_seconds = time_passed / 1000. 89 | 90 | rotation_direction = Vector3() 91 | 92 | #Adjust the rotation direction depending on key presses 93 | pressed_keys = pygame.key.get_pressed() 94 | 95 | if pressed_keys[K_q]: 96 | rotation_direction.x = +1.0 97 | elif pressed_keys[K_a]: 98 | rotation_direction.x = -1.0 99 | 100 | if pressed_keys[K_w]: 101 | rotation_direction.y = +1.0 102 | elif pressed_keys[K_s]: 103 | rotation_direction.y = -1.0 104 | 105 | if pressed_keys[K_e]: 106 | rotation_direction.z = +1.0 107 | elif pressed_keys[K_d]: 108 | rotation_direction.z = -1.0 109 | 110 | # Apply time based movement to rotation 111 | rotation += rotation_direction * rotation_speed * time_passed_seconds 112 | 113 | # Build the rotation matrix 114 | rotation_matrix = Matrix.x_rotation(rotation.x) 115 | rotation_matrix *= Matrix.y_rotation(rotation.y) 116 | rotation_matrix *= Matrix.z_rotation(rotation.z) 117 | 118 | transformed_points = [] 119 | 120 | # Transform all the points and adjust for camera position 121 | for point in points: 122 | 123 | p = rotation_matrix.transform_vec3(point) - camera_position 124 | 125 | transformed_points.append(p) 126 | 127 | transformed_points.sort(key=point_z) 128 | 129 | # Perspective project and blit all the points 130 | for x, y, z in transformed_points: 131 | 132 | if z < 0: 133 | x = center_x + x * -viewing_distance / z 134 | y = center_y + -y * -viewing_distance / z 135 | 136 | screen.blit(ball, (x-ball_center_x, y-ball_center_y)) 137 | 138 | 139 | # Function to draw a single axes, see below 140 | def draw_axis(color, axis, label): 141 | 142 | axis = rotation_matrix.transform_vec3(axis * 150.) 143 | SCREEN_SIZE = (640, 480) 144 | center_x = SCREEN_SIZE[0] / 2.0 145 | center_y = SCREEN_SIZE[1] / 2.0 146 | x, y, z = axis - camera_position 147 | 148 | x = center_x + x * -viewing_distance / z 149 | y = center_y + -y * -viewing_distance / z 150 | 151 | pygame.draw.line(screen, color, (center_x, center_y), (x, y), 2) 152 | 153 | w, h = label.get_size() 154 | screen.blit(label, (x-w/2, y-h/2)) 155 | 156 | # Draw the x, y and z axes 157 | x_axis = Vector3(1, 0, 0) 158 | y_axis = Vector3(0, 1, 0) 159 | z_axis = Vector3(0, 0, 1) 160 | 161 | draw_axis(red, x_axis, x_surface) 162 | draw_axis(green, y_axis, y_surface) 163 | draw_axis(blue, z_axis, z_surface) 164 | 165 | # Display rotation information on screen 166 | degrees_txt = tuple(degrees(r) for r in rotation) 167 | rotation_txt = "Rotation: Q/A %.3f, W/S %.3f, E/D %.3f" % degrees_txt 168 | txt_surface = font.render(rotation_txt, True, white) 169 | screen.blit(txt_surface, (5, 5)) 170 | 171 | # Displat the rotation matrix on screen 172 | matrix_txt = str(rotation_matrix) 173 | txt_y = 25 174 | for line in matrix_txt.split('\n'): 175 | txt_surface = font.render(line, True, white) 176 | screen.blit(txt_surface, (5, txt_y)) 177 | txt_y += 20 178 | 179 | pygame.display.update() 180 | 181 | if __name__ == "__main__": 182 | run() 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /Chapter 9/9-4.py: -------------------------------------------------------------------------------- 1 | from OpenGL.GL import * 2 | from OpenGL.GLU import * 3 | import pygame 4 | from pygame.locals import * 5 | 6 | screen = pygame.display.set_mode((640, 480), HWSURFACE|DOUBLEBUF|OPENGL) 7 | 8 | def resize(width, height): 9 | glViewport(0, 0, width, height) 10 | glMatrixMode(GL_PROJECTION) 11 | glLoadIdentity() 12 | gluPerspective(60, float(width)/height, 1, 10000) 13 | glMatrixMode(GL_MODELVIEW) 14 | glLoadIdentity() 15 | 16 | 17 | -------------------------------------------------------------------------------- /Chapter 9/9-5.py: -------------------------------------------------------------------------------- 1 | from OpenGL.GL import * 2 | from OpenGL.GLU import * 3 | import pygame 4 | from pygame.locals import * 5 | 6 | screen = pygame.display.set_mode((640, 480), HWSURFACE|DOUBLEBUF|OPENGL) 7 | 8 | def resize(width, height): 9 | glViewport(0, 0, width, height) 10 | glMatrixMode(GL_PROJECTION) 11 | glLoadIdentity() 12 | gluPerspective(60, float(width)/height, 1, 10000) 13 | glMatrixMode(GL_MODELVIEW) 14 | glLoadIdentity() 15 | 16 | 17 | def init(): 18 | 19 | glEnable(GL_DEPTH_TEST) 20 | glClearColor(1.0, 1.0, 1.0, 0.0) 21 | 22 | glShadeModel(GL_FLAT) 23 | glEnable(GL_COLOR_MATERIAL) 24 | 25 | glEnable(GL_LIGHTING) 26 | glEnable(GL_LIGHT0) 27 | glLight(GL_LIGHT0, GL_POSITION, (0, 1, 1, 0)) 28 | -------------------------------------------------------------------------------- /Chapter 9/9-6.py: -------------------------------------------------------------------------------- 1 | glBegin(GL_QUADS) 2 | glColor(1.0, 0.0, 0.0) # Red 3 | glVertex(100.0, 100.0, 0.0) # Top left 4 | glVertex(200.0, 100.0, 0.0) # Top right 5 | glVertex(200.0, 200.0, 0.0) # Bottom right 6 | glVertex(100.0, 200.0, 0.0) # Bottom left 7 | 8 | glEnd() 9 | -------------------------------------------------------------------------------- /Chapter 9/9-7.py: -------------------------------------------------------------------------------- 1 | # Create a display list 2 | tank_display_list = glGenLists(1) 3 | glNewList(tank_display_list, GL_COMPILE) 4 | 5 | draw_tank() 6 | 7 | # End the display list 8 | glEndList() 9 | -------------------------------------------------------------------------------- /Chapter 9/ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 9/ball.png -------------------------------------------------------------------------------- /Chapter 9/gameobjects/__pycache__/matrix44.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 9/gameobjects/__pycache__/matrix44.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 9/gameobjects/__pycache__/util.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 9/gameobjects/__pycache__/util.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 9/gameobjects/__pycache__/vector2.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 9/gameobjects/__pycache__/vector2.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 9/gameobjects/__pycache__/vector3.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 9/gameobjects/__pycache__/vector3.cpython-34.pyc -------------------------------------------------------------------------------- /Chapter 9/gameobjects/util.py: -------------------------------------------------------------------------------- 1 | from math import * 2 | 3 | def format_number(n, accuracy=6): 4 | """Formats a number in a friendly manner 5 | (removes trailing zeros and unneccesary point.""" 6 | 7 | fs = "%."+str(accuracy)+"f" 8 | str_n = fs%float(n) 9 | if '.' in str_n: 10 | str_n = str_n.rstrip('0').rstrip('.') 11 | if str_n == "-0": 12 | str_n = "0" 13 | #str_n = str_n.replace("-0", "0") 14 | return str_n 15 | 16 | 17 | def lerp(a, b, i): 18 | """Linear enterpolate from a to b.""" 19 | return a+(b-a)*i 20 | 21 | 22 | def range2d(range_x, range_y): 23 | 24 | """Creates a 2D range.""" 25 | 26 | range_x = list(range_x) 27 | return [ (x, y) for y in range_y for x in range_x ] 28 | 29 | 30 | def xrange2d(range_x, range_y): 31 | 32 | """Iterates over a 2D range.""" 33 | 34 | range_x = list(range_x) 35 | for y in range_y: 36 | for x in range_x: 37 | yield (x, y) 38 | 39 | 40 | def saturate(value, low, high): 41 | return min(max(value, low), high) 42 | 43 | 44 | def is_power_of_2(n): 45 | """Returns True if a value is a power of 2.""" 46 | return log(n, 2) % 1.0 == 0.0 47 | 48 | 49 | def next_power_of_2(n): 50 | """Returns the next power of 2 that is >= n""" 51 | return int(2 ** ceil(log(n, 2))) 52 | 53 | if __name__ == "__main__": 54 | 55 | print(list( xrange2d(range(3), range(3)) )) 56 | print(range2d(range(3), range(3))) 57 | print(is_power_of_2(7)) 58 | print(is_power_of_2(8)) 59 | print(is_power_of_2(9)) 60 | 61 | print(next_power_of_2(7)) 62 | -------------------------------------------------------------------------------- /Chapter 9/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonProgramming/Beginning-Game-Development-with-Python-and-Pygame/c239925041a6fa361386f65316ef4bea12c3b482/Chapter 9/map.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 PythonProgramming.net 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Beginning-Game-Development-with-Python-and-Pygame 2 | Source Code from Beginning Game Development with Python and Pygame by Harrison Kinsley and Will McGugan 3 | 4 | Code is organized by chapter, denoted by the listing name. As the book progresses, we use more and more of the gameobjects library. 5 | -------------------------------------------------------------------------------- /gameobjects/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | 'vector2', 3 | 'vector3', 4 | 'util', 5 | 'sphere', 6 | 'matrix44', 7 | 'color', 8 | 'gametime', 9 | 'grid' 10 | ] 11 | 12 | 13 | __version__ = "0.0.3" 14 | -------------------------------------------------------------------------------- /gameobjects/gametime.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class GameClock(object): 4 | 5 | """Manages time in a game.""" 6 | 7 | def __init__(self, game_ticks_per_second=20): 8 | 9 | """Create a Game Clock object. 10 | 11 | game_ticks_per_second -- The number of logic frames a second. 12 | 13 | """ 14 | 15 | self.game_ticks_per_second = float(game_ticks_per_second) 16 | self.game_tick = 1. / self.game_ticks_per_second 17 | self.speed = 1. 18 | 19 | self.clock_time = 0. 20 | self.virtual_time = 0. 21 | self.game_time = 0. 22 | self.game_frame_count = 0 23 | self.real_time_passed = 0. 24 | 25 | self.real_time = self.get_real_time() 26 | self.started = False 27 | self.paused = False 28 | self.between_frame = 0.0 29 | 30 | self.fps_sample_start_time = 0.0 31 | self.fps_sample_count = 0 32 | self.average_fps = 0 33 | 34 | 35 | def start(self): 36 | 37 | """Starts the Game Clock. Must be called once.""" 38 | 39 | if self.started: 40 | return 41 | 42 | self.clock_time = 0. 43 | self.virtual_time = 0. 44 | self.game_time = 0. 45 | self.game_frame_count = 0 46 | self.real_time_passed = 0. 47 | 48 | self.real_time = self.get_real_time() 49 | self.started = True 50 | 51 | self.fps = 0.0 52 | self.fps_sample_start_time = self.real_time 53 | self.fps_sample_count = 0 54 | 55 | 56 | def set_speed(self, speed): 57 | 58 | """Sets the speed of the clock. 59 | 60 | speed -- A time factor (1 is normal speed, 2 is twice normal) 61 | 62 | """ 63 | assert isinstance(speed, float), "Must be a float" 64 | 65 | if speed < 0.0: 66 | raise ValueError("Negative speeds not supported") 67 | 68 | self.speed = speed 69 | 70 | 71 | def pause(self): 72 | 73 | """Pauses the Game Clock.""" 74 | 75 | self.pause = True 76 | 77 | def unpause(self): 78 | 79 | """Un-pauses the Game Clock.""" 80 | 81 | self.pause = False 82 | 83 | 84 | def get_real_time(self): 85 | 86 | """Returns the real time, as reported by the system clock. 87 | This method may be overriden.""" 88 | 89 | import time 90 | return time.clock() 91 | 92 | 93 | def get_fps(self): 94 | 95 | """Retrieves the current frames per second as a tuple containing 96 | the fps and average fps over a second.""" 97 | 98 | return self.fps, self.average_fps 99 | 100 | 101 | def get_between_frame(self): 102 | 103 | """Returns the interpolant between the previous game tick and the 104 | next game tick.""" 105 | 106 | return self.between_frame 107 | 108 | 109 | def update(self, max_updates = 0): 110 | 111 | """Advances time, must be called once per frame. Yields tuples of 112 | game frame count and game time. 113 | 114 | max_updates -- Maximum number of game time updates to issue. 115 | 116 | """ 117 | 118 | assert self.started, "You must call 'start' before using a GameClock." 119 | 120 | real_time_now = self.get_real_time() 121 | 122 | self.real_time_passed = real_time_now - self.real_time 123 | self.real_time = real_time_now 124 | 125 | self.clock_time += self.real_time_passed 126 | 127 | if not self.paused: 128 | self.virtual_time += self.real_time_passed * self.speed 129 | 130 | update_count = 0 131 | while self.game_time + self.game_tick < self.virtual_time: 132 | 133 | self.game_frame_count += 1 134 | self.game_time = self.game_frame_count * self.game_tick 135 | yield (self.game_frame_count, self.game_time) 136 | 137 | if max_updates and update_count == max_updates: 138 | break 139 | 140 | self.between_frame = ( self.virtual_time - self.game_time ) / self.game_tick 141 | 142 | if self.real_time_passed != 0: 143 | self.fps = 1.0 / self.real_time_passed 144 | else: 145 | self.fps = 0.0 146 | 147 | self.fps_sample_count += 1 148 | 149 | if self.real_time - self.fps_sample_start_time > 1.0: 150 | 151 | self.average_fps = self.fps_sample_count / (self.real_time - self.fps_sample_start_time) 152 | self.fps_sample_start_time = self.real_time 153 | self.fps_sample_count = 0 154 | 155 | 156 | if __name__ == "__main__": 157 | 158 | import time 159 | t = GameClock(20) # AI is 20 frames per second 160 | t.start() 161 | 162 | while t.virtual_time < 2.0: 163 | 164 | for (frame_count, game_time) in t.update(): 165 | 166 | print("Game frame #%i, %2.4f" % (frame_count, game_time)) 167 | 168 | virtual_time = t.virtual_time 169 | print("\t%2.2f%% between game frame, time is %2.4f"%(t.between_frame*100., virtual_time)) 170 | 171 | 172 | time.sleep(0.2) # Simulate time to render frame 173 | 174 | -------------------------------------------------------------------------------- /gameobjects/locals.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | ( WRAP_REPEAT, 4 | WRAP_CLAMP, 5 | WRAP_ERROR ) = list(range(3)) 6 | -------------------------------------------------------------------------------- /gameobjects/sphere.py: -------------------------------------------------------------------------------- 1 | from . import vector3 2 | from .util import format_number 3 | 4 | class Sphere(object): 5 | 6 | def __init__(self, position=(0,0,0), radius= 1.): 7 | 8 | self._position = vector3.Vector3(position) 9 | self._radius = float(radius) 10 | 11 | 12 | def get_position(self): 13 | return self._position 14 | def set_position(self, position): 15 | self._position.set(*position) 16 | position = property(get_position, set_position, None, "Position of sphere centre.") 17 | 18 | def get_radius(self): 19 | return self._radius 20 | def set_radius(self, radius): 21 | self._radius = float(radius) 22 | radius = property(get_radius, set_radius, None, "Radius of the sphere.") 23 | 24 | 25 | def __str__(self): 26 | 27 | return "( position %s, radius %s )" % (self.position, format_number(self.radius)) 28 | 29 | 30 | def __repr__(self): 31 | 32 | return "Sphere(%s, %s)" % (tuple(self.position), self.radius) 33 | 34 | 35 | def __contains__(self, shape): 36 | 37 | try: 38 | return shape.in_sphere(self) 39 | except AttributeError: 40 | raise TypeError( "No 'in_sphere' method supplied by %s" % type(shape) ) 41 | 42 | 43 | def contains(self, shape): 44 | 45 | return shape in self 46 | 47 | 48 | def in_sphere(self, sphere): 49 | 50 | return self.position.get_distance(sphere.position) + self.radius <= sphere.radius 51 | 52 | 53 | def intersects(self, shape): 54 | 55 | try: 56 | return shape.intersects_sphere(self) 57 | except AttributeError: 58 | raise TypeError( "No 'intersects_sphere' method supplied by %s" % type(shape) ) 59 | 60 | 61 | def intersects_sphere(self, sphere): 62 | 63 | return self.position.get_distance(sphere.position) < self.radius + sphere.radius 64 | 65 | 66 | if __name__ == "__main__": 67 | 68 | s1 = Sphere() 69 | s2 = Sphere( (1,1,1) ) 70 | s3 = Sphere( radius=10 ) 71 | s4 = eval(repr(s2)) 72 | 73 | print(s1) 74 | print(repr(s2)) 75 | print(s2, s4) 76 | 77 | v = vector3.Vector3(0, 1, 0) 78 | print(v in s1) 79 | 80 | big = Sphere(radius=1) 81 | small = Sphere(position=(.8, 0, 0), radius=.2) 82 | 83 | 84 | print(small, big) 85 | print(small in big) 86 | 87 | -------------------------------------------------------------------------------- /gameobjects/test_vector3.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from .vector3 import Vector3 4 | 5 | class TestVector3(unittest.TestCase): 6 | 7 | def test_add(self): 8 | v1 = Vector3(1, 2, 3) 9 | v2 = Vector3(4, 5, 6) 10 | self.assertEqual(v1+v2, (5, 7, 9)) 11 | self.assertEqual((1, 2, 3)+v2, (5, 7, 9)) 12 | 13 | 14 | def test_sub(self): 15 | v1 = Vector3(1, 2, 3) 16 | v2 = Vector3(4, 5, 6) 17 | self.assertEqual(v1-v2, (-3, -3, -3)) 18 | self.assertEqual((1, 2, 3)-v2, (-3, -3, -3)) 19 | 20 | 21 | def test_mul(self): 22 | v1 = Vector3(1, 2, 3) 23 | v2 = Vector3(4, 5, 6) 24 | self.assertEqual(3*v1, (3, 6, 9)) 25 | self.assertEqual(v1*3, (3, 6, 9)) 26 | self.assertEqual((3, 3, 3)*v1, (3, 6, 9)) 27 | self.assertEqual(v1*(3, 3, 3), (3, 6, 9)) 28 | v1 *= 2 29 | self.assertEqual(v1, (2, 4, 6)) 30 | 31 | def test_div(self): 32 | v1 = Vector3(1, 2, 3) 33 | v2 = Vector3(4, 5, 6) 34 | self.assertEqual(3/v1, (3., 3./2, 1.)) 35 | self.assertEqual(v1/3, (1./3, 2/3., 3/3.)) 36 | self.assertEqual((3, 3, 3)/v1, (3./1., 3./2., 3./3.)) 37 | self.assertEqual(v1/(3, 3, 3), (1./3, 2/3., 3/3.)) 38 | v1 /= 2 39 | self.assertEqual(v1, (.5, 1., 1.5)) 40 | 41 | def test_length(self): 42 | v1 = Vector3(3., 4., 0.) 43 | self.assertEqual(v1.length, 5.) 44 | v1.length = 10. 45 | self.assertEqual(v1, (6, 8, 0)) 46 | v1.normalise() 47 | assert v1.length, 1 48 | 49 | def test_copy(self): 50 | v1 = Vector3(3., 4., 0.) 51 | assert v1.copy(), (3, 4, 0) 52 | 53 | def test_properties(self): 54 | v1 = Vector3(1, 2, 3) 55 | self.assertEqual(v1.x, 1) 56 | self.assertEqual(v1.y, 2) 57 | self.assertEqual(v1.z, 3) 58 | v1.x *= 2 59 | v1.y *= 2 60 | v1.z *= 2 61 | self.assertEqual(v1.x, 2) 62 | self.assertEqual(v1.y, 4) 63 | self.assertEqual(v1.z, 6) 64 | 65 | def test_swizzle(self): 66 | v1 = Vector3(1, 2, 3) 67 | self.assertEqual(v1('xxxyyyzzz'), (1, 1, 1, 2, 2, 2, 3, 3, 3)) 68 | self.assertEqual(v1('yyy'), (2, 2, 2)) 69 | self.assertEqual(v1('zyx'), (3, 2, 1)) 70 | 71 | if __name__ == '__main__': 72 | unittest.main() 73 | -------------------------------------------------------------------------------- /gameobjects/util.py: -------------------------------------------------------------------------------- 1 | from math import * 2 | 3 | def format_number(n, accuracy=6): 4 | """Formats a number in a friendly manner 5 | (removes trailing zeros and unneccesary point.""" 6 | 7 | fs = "%."+str(accuracy)+"f" 8 | str_n = fs%float(n) 9 | if '.' in str_n: 10 | str_n = str_n.rstrip('0').rstrip('.') 11 | if str_n == "-0": 12 | str_n = "0" 13 | #str_n = str_n.replace("-0", "0") 14 | return str_n 15 | 16 | 17 | def lerp(a, b, i): 18 | """Linear enterpolate from a to b.""" 19 | return a+(b-a)*i 20 | 21 | 22 | def range2d(range_x, range_y): 23 | 24 | """Creates a 2D range.""" 25 | 26 | range_x = list(range_x) 27 | return [ (x, y) for y in range_y for x in range_x ] 28 | 29 | 30 | def xrange2d(range_x, range_y): 31 | 32 | """Iterates over a 2D range.""" 33 | 34 | range_x = list(range_x) 35 | for y in range_y: 36 | for x in range_x: 37 | yield (x, y) 38 | 39 | 40 | def saturate(value, low, high): 41 | return min(max(value, low), high) 42 | 43 | 44 | def is_power_of_2(n): 45 | """Returns True if a value is a power of 2.""" 46 | return log(n, 2) % 1.0 == 0.0 47 | 48 | 49 | def next_power_of_2(n): 50 | """Returns the next power of 2 that is >= n""" 51 | return int(2 ** ceil(log(n, 2))) 52 | 53 | if __name__ == "__main__": 54 | 55 | print(list( xrange2d(range(3), range(3)) )) 56 | print(range2d(range(3), range(3))) 57 | print(is_power_of_2(7)) 58 | print(is_power_of_2(8)) 59 | print(is_power_of_2(9)) 60 | 61 | print(next_power_of_2(7)) 62 | --------------------------------------------------------------------------------