Don't know any video tutorials. Just what I see people use.
I learn form this forum and docs. Using internet looking up specific question.
How I learn form this forum. Was answer questions but not post it. See what other people answer where.
- I learn to be explicit. I normally avoid abbreviation. Just make program harder to read.
- Pygame Rect are very powerful. They work great for placement and layouts.
- Pygame Vector2 will help reduce math and make it bit simpler once learn.
- Learn to pass images. This reference them.
- Learn to refactor code. Having one class handle multiply things. It just get messy.
- Learn to build a boiler plate. Just lets me code quickly.
My Boilerplate.
import pygame
import os
class Scene:
# SceneManager Interface
def __init__(self, manager):
self.manager = manager
def scene_draw(self, surface):
self.on_draw(surface)
def scene_drop(self):
self.on_drop()
def scene_event(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.on_quit()
else:
self.on_event(event)
def scene_focus(self, *args, **kwargs):
self.on_focus(*args, **kwargs)
def scene_new(self,*args, **kwargs):
self.on_new(*args, **kwargs)
def scene_update(self, delta):
self.on_update(delta)
def flip(self, scene, *args, **kwargs):
self.manager.flip(scene, *args, **kwargs)
def flip_new(self, scene, *args, **kwargs):
self.manager.flip_new(scene, *args, **kwargs)
def on_quit(self):
self.manager.quit()
# Scene Interface
def on_draw(self, surface): pass
def on_drop(self): pass
def on_event(self, event): pass
def on_focus(self): pass
def on_new(self): pass
def on_update(self, delta): pass
class Manager:
def __init__(self, title, width, height, center=True, flags=0):
if center is True:
os.environ['SDL_VIDEO_CENTERED'] = '1'
elif center:
os.environ['SDL_VIDEO_WINDOW_POS'] = '{0}, {1}'.format(*center)
# Basic pygame setup
pygame.display.set_caption(title)
self.surface = pygame.display.set_mode((width, height), flags)
self.rect = self.surface.get_rect()
self.clock = pygame.time.Clock()
self.running = False
self.delta = 0
self.fps = 60
# Scene handling
self._scene = Scene(self)
self.scenes = {}
self.extension = []
def add_scene(self, scene, name=None):
if name is None:
name = scene.__class__.__name__
self.scenes[name] = scene
def _flip(self, scene):
self._scene.scene_drop()
if isinstance(scene, Scene):
self._scene = scene
else:
# String: Load live scene
self._scene = self.scenes[scene]
def flip(self, scene, *args, **kwargs):
self._flip(scene)
self._scene.scene_focus(*args, **kwargs)
def flip_new(self, scene, *args, **kwargs):
self._flip(scene)
self._scene.scene_new(*args, **kwargs)
def mainloop(self):
self.running = True
while self.running:
self._scene.scene_event()
self._scene.scene_update(self.delta)
self._scene.scene_draw(self.surface)
for extension in self.extension:
extension(self)
pygame.display.flip()
self.delta = self.clock.tick(self.fps)
def quit(self):
self.running = False
Example. Didn't use one global variable.
import pygame
from scene import Scene, Manager
from pygame.sprite import Sprite, Group
class GameSprite(Sprite):
def __init__(self, image, position, anchor="topleft"):
Sprite.__init__(self)
self.image = image
self.rect = image.get_rect(**{anchor: position})
self.updates = []
def draw(self, surface):
surface.blit(self.image, self.rect)
def update(self, keys_pressed, delta):
for update in self.updates:
update(keys_pressed, delta)
class LifePool:
def __init__(self, value):
self.max_value = value
self.value = value
def __add__(self, value):
self.value = min(self.value + value, self.max_value)
def __sub__(self, value):
self.value = max(self.value - value, 0)
def get_percent(self):
return self.value / self.max_value
class CharacterMovement:
def __init__(self, sprite, speed):
self.sprite = sprite
self.speed = speed * 0.01
self.center = pygame.Vector2(sprite.rect.center)
self._vector = pygame.Vector2(0, 0)
def move_up(self):
self._vector.y -= 1
def move_down(self):
self._vector.y += 1
def move_left(self):
self._vector.x -= 1
def move_right(self):
self._vector.x += 1
def update(self, delta):
if self._vector != pygame.Vector2(0, 0):
self._vector.normalize_ip()
self.center += self._vector * self.speed * delta
self.sprite.rect.center = self.center
self._vector = pygame.Vector2(0, 0)
class CharacterMovementKeys:
def __init__(self, movement, up=pygame.K_w, down=pygame.K_s, left=pygame.K_a, right=pygame.K_d):
self.movement = movement
self.up = self.list_form(up)
self.down = self.list_form(down)
self.left = self.list_form(left)
self.right = self.list_form(right)
def list_form(self, key):
if isinstance(key, list):
return key
elif isinstance(key, tuple):
return list(key)
return [key]
def move(self, key_pressed, delta):
if any([key_pressed[key] for key in self.up]):
self.movement.move_up()
if any([key_pressed[key] for key in self.down]):
self.movement.move_down()
if any([key_pressed[key] for key in self.left]):
self.movement.move_left()
if any([key_pressed[key] for key in self.right]):
self.movement.move_right()
self.movement.update(delta)
class Character:
def __init__(self, image, position, anchor="topleft"):
self.sprite = GameSprite(image, position, anchor)
self.life = LifePool(20)
self.create_life_sprite()
self.movement = CharacterMovement(self.sprite, 8)
self.keys = CharacterMovementKeys(self.movement)
self.sprite.updates.append(self.keys.move)
self.sprite.updates.append(self.update_life_position)
def create_hit_points_image(self):
size = self.sprite.rect.width, 5
surface = pygame.Surface(size)
surface.fill(pygame.Color('firebrick'))
percent = self.life.get_percent()
width = int(size[0] * percent)
rect = (0, 0, width, size[1])
pygame.draw.rect(surface, pygame.Color('lawngreen'), rect)
return surface
def create_life_sprite(self):
image = self.create_hit_points_image()
position = self.sprite.rect.midtop
position = position[0], position[1] - image.get_height() - 2
self.life_sprite = GameSprite(image, position, "midtop")
def update_life_position(self, keys_pressed, delta):
position = self.sprite.rect.midtop
position = position[0], position[1] - self.life_sprite.rect.height - 2
self.life_sprite.rect.midtop = position
class MainScene(Scene):
def __init__(self, manager):
Scene.__init__(self, manager)
image = self.create_character_image()
self.character = Character(image, manager.rect.center, "center")
self.sprites = Group(self.character.sprite)
self.life_bar = Group(self.character.life_sprite)
self.show_life_bar = True
def create_character_image(self):
surface = pygame.Surface((30, 30))
surface.fill(pygame.Color("dodgerblue"))
return surface
def on_draw(self, surface):
surface.fill(pygame.Color("black"))
self.sprites.draw(surface)
if self.show_life_bar:
self.life_bar.draw(surface)
def on_event(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.show_life_bar = not self.show_life_bar
def on_update(self, delta):
keys_pressed = pygame.key.get_pressed()
self.sprites.update(keys_pressed, delta)
def main():
pygame.init()
manager = Manager("Example", 800, 600)
manager.flip(MainScene(manager))
manager.mainloop()
if __name__ == "__main__":
main()