def __init__(self, level_name, player, static_world_components, dynamic_world_components, background=None, level_size=None, camera_type=None): if level_size == None: level_size = background.size self.background = background self.size = level_size self.name = level_name # a dictionary with all image related game components self.player = player self.characters = [ ] # starts empty; is filled by npc, and the player(s) self.npc = pygame.sprite.Group() self.static_components = static_world_components # image parts (e.g. background, ground) self.dynamic_components = dynamic_world_components # image parts (e.g. swings, moving objects, bullets) self.components = [] + self.static_components + self.dynamic_components # redundant list; fast requesting component self.add_character(player) # build the static game world self.image, self.rect = self.build_background( level_size, background=background, static_components=self.static_components) if camera_type is not None: self.camera = Camera(camera_type, player.rect, level_size) # the screen view # todo: decide on who should hold the camera graphics_handler.set_camera( self.camera ) # makes it possible to blit directly to the graphics handler else: self.camera = None self.freeze = False # freezes all updates to components if INFO: print("[LV] image '{}' loaded".format(level_name)) self.killed_monster = 0
scene = loading.load("test_map03") chest = objects.getObjectsOfType(Chest)[0] chest._items = [Item("Potion"), Item("Potion"), Item("Book")] animTile = GameObject(z = 10, position = Vector2(0, 50)) animTile.sprite = sprite2.loadSpriteAnimation("player_idle") animTile.play("player_idle") _player = objects.getObjectsOfType(player.Player)[0] _player.sprite = sprite2.loadSpriteAnimation("eggy") _player.play("eggy") ##### camera = Camera(15) camera.setTarget(player.getPlayer()) fps_delay = 60 #Show fps every second fps_delta = 0 while True: inputcontrol.evaluate(pygame.event.get()) physics.solve(objects.getObjectsOfType(Entity)) camera.render(gameScreen) ui.render(gameScreen) scaled_display = pygame.transform.scale(gameScreen, (SCREENWIDTH * SCALEFACTOR, SCREENHEIGHT * SCALEFACTOR)) display.blit(scaled_display, (0, 0)) pygame.display.update() clock.tick(60)
from pygame.locals import * from pygame import * from graphics import Camera from pygame.math import Vector2 from gameobject import GameObject pygame.init() display = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT)) pygame.display.set_caption("Ultimate Pong") clock = pygame.time.Clock() fps_delay = 60 fps_delta = 0 camera = Camera() background = GameObject(position=Vector2(0, 0), scale=Vector2(SCREENWIDTH, SCREENHEIGHT)) background.colorize(pygame.Color(255, 255, 255)) ball = GameObject(position=Vector2(40, 40)) ball.colorize(pygame.Color(255, 0, 0)) while True: #TODO: Move inputs into a custom class and handle with single function call for event in pygame.event.get(): if event.type == QUIT: print("Closing game...") pygame.quit() sys.exit(0)
class Level: """A class with image resources, and helper functions""" def __init__(self, level_name, player, static_world_components, dynamic_world_components, background=None, level_size=None, camera_type=None): if level_size == None: level_size = background.size self.background = background self.size = level_size self.name = level_name # a dictionary with all image related game components self.player = player self.characters = [ ] # starts empty; is filled by npc, and the player(s) self.npc = pygame.sprite.Group() self.static_components = static_world_components # image parts (e.g. background, ground) self.dynamic_components = dynamic_world_components # image parts (e.g. swings, moving objects, bullets) self.components = [] + self.static_components + self.dynamic_components # redundant list; fast requesting component self.add_character(player) # build the static game world self.image, self.rect = self.build_background( level_size, background=background, static_components=self.static_components) if camera_type is not None: self.camera = Camera(camera_type, player.rect, level_size) # the screen view # todo: decide on who should hold the camera graphics_handler.set_camera( self.camera ) # makes it possible to blit directly to the graphics handler else: self.camera = None self.freeze = False # freezes all updates to components if INFO: print("[LV] image '{}' loaded".format(level_name)) self.killed_monster = 0 @staticmethod def build_background(level_size, background=None, static_components=None, colour=(255, 150, 0)): if static_components is None: static_components = [] level = pygame.Surface(level_size) level.fill(colour) level_rect = level.get_rect() if background is not None: level.blit(background.image, background.rect) if INFO: print('[LV] Background blitted in build: {}'.format( background.rect)) for placement in [Background, Ground]: # first blit Background, afterwards Ground for component in static_components: # if not self.level_rect.contains(component.rect): continue # only if component fits in level if type(component) == placement: level.blit(component.image.convert_alpha(), component.rect) if INFO: print("image surface created: {}".format(level)) return level, level_rect # game component management) def add_world_component(self, world_component): self.static_components.append(world_component) if DEBUG: print("added image to game world: " + repr(world_component.image)) self.image.blit(world_component.image, world_component.rect) self.components.append(world_component) # when adding a dynamic level component to the level, this method should be used exclusively def add_component(self, component): self.dynamic_components.append(component) self.components.append(component) component.level = self # can't get sprite groups to work def del_component(self, component): if type(component) == Player: self.player = None try: del self.dynamic_components[self.dynamic_components.index( component)] # component should have __eq__ overridden except ValueError as ex: if WARNING: print( f"[LL] couldn't find component in dynamic components list {component}" ) try: del self.components[self.components.index(component)] except ValueError as ex: if WARNING: print( f"[LL] couldn't find component in components list {component}" ) def add_character(self, character): self.characters.append(character) if character.TYPE == 'Monster': character.enemy = self.player character.level = self #self.npc.add(character) def del_character(self, character): index = self.characters.index(character) del self.characters[index] if character is self.player: self.player = None if isinstance(character, Monster): self.killed_monster += 1 def del_component_index(self): # maybe implement this pass def get_dynamic_components(self): """builds a (flat) list with all dynamic game component""" return [x for x in self.dynamic_components] def get_game_components(self): """Builds a new list with all static and dynamic components""" # first get the static components, as these will be blitted over by the dynamic ones return [x for x in self.components] def update(self, dt): """update all game components in the current level (does not checks for collisions)""" if self.player is None: self.end() if self.freeze: if self.player: self.add_component( Text("you can unfreeze the screen with 'f'", (self.camera.rect.centerx - 80, self.camera.rect.centery - 160), (200, 200), max_time=1, font_size=25)) return for character in self.characters: if character.is_alive(): character.update(dt) else: self.del_character(character) for component in self.dynamic_components: component.update(dt) # update camera as last if self.camera and self.player: self.camera.update(self.player.rect) if len(self.characters ) <= 1: # there should always be one monster in the game self.add_character( Schagel((random.randint(0, self.size[0]), self.size[1] / 2), (58, 52))) def check_level_finished(self): if self.killed_monster >= 8: return True return False # collision detection per entity that the function is called with @staticmethod def detect_collision(entity, components): if entity.ground: return for component in components: if pygame.sprite.collide_rect(entity, component): if component: # makes it acceptable to have None in components if DEBUG: print("[CD] ground collision!") return component else: if DEBUG: print("[CD] unknown collision: {}".format(component)) @staticmethod def detect_entity_collision(entity): pass # returns None, or the first found ground @staticmethod def find_type_collision(entity, components, component_type): for component in components: if pygame.sprite.collide_rect( entity, component) and type(component) == component_type: if DEBUG: print("[CD] {} collision!".format(component_type)) return component # calls on_collision on all entities @staticmethod def detect_type_collisions(entities, components, component_types): """detects the collision between all static components, of one or multiple types, and 'entities'; calls on_collision on every entity""" for entity in entities: for component in components: if type(component ) in component_types and pygame.sprite.collide_rect( entity, component): if DEBUG: print(f"[CD] collision: '{entity}', '{component}'!") component.on_collision(entity) if isinstance(component, PhysicsEntity): entity.on_collision(component) def detect_characters_ground(self): for character in self.characters: for component in self.static_components: if type(component) in [Ground, BuildingBlock ] and pygame.sprite.collide_rect( character, component): if DEBUG: print( f"[CD] ground collision: '{character}', '{component}'!" ) character.on_collision(component) break character.ground = None # checks collision between all characters with zero double-checks def detect_character_collisions(self): """Check collision between every character once. Calls the collision method on each with as argument the other""" temp = [character for character in self.characters] for character0 in self.characters: for character1 in temp: if character0 != character1 and pygame.sprite.collide_rect( character0, character1): character0.on_collision(character1) character1.on_collision(character0) if DEBUG: print( f"[LV] collision between: {character0} and {character1}" ) del temp[0] def detect_characters_out_of_bound(self): for character in self.characters: if not pygame.sprite.collide_rect(character, self): self.del_character(character) if DEBUG: print(f"char out of bound: {character}") if 0 > character.rect.left: character.rect.left = 0 elif character.rect.left > self.size[0]: # level x size character.rect.right = self.size[0] # check collisions for affected parties def detect_collisions(self): self.detect_characters_out_of_bound() self.detect_character_collisions() self.detect_characters_ground() self.detect_type_collisions(self.characters, self.dynamic_components, [Vial]) self.detect_type_collisions(self.dynamic_components, self.static_components, [Ground]) # displays all image components from back- to foreground def display(self): """calls display on every component; blitting them, and adding their rectangle to a dirty rectangles list""" # first put the background on the display if self.camera is None: camera_rect = None else: camera_rect = self.camera.rect graphics_controller.blit(self.image, self.rect, self.camera.rect) # display level base image for dynamic_component in self.dynamic_components: # things like throw-ables dynamic_component.display(camera_rect) for character in self.characters: # player and NPCs character.display(camera_rect) for static_component in self.static_components: # foreground is the last to be displayed if type(static_component) == ForeGround: static_component.display(camera_rect) def end(self): self.freeze = True self.add_component( Text("End Of Game", (self.camera.rect.centerx, self.size[1] / 2), (100, 50), font_size=30)) def __del__(self): graphics_handler.unset_camera() if INFO: print("[LL] image '{}' unloaded".format(self.name))
def load(self): self.world_size = Size(3000, 3000) self.camera = Camera(self.size, self.world_size, 1000, 10) self._tool = None self.tool = None self.batch = graphics.Batch() self.background = CameraGroup(graphics.OrderedGroup(0), self.camera) self.foreground = CameraGroup(graphics.OrderedGroup(1), self.camera) self.playerg = CameraGroup(graphics.OrderedGroup(2), self.camera) self.world_ui = CameraGroup(graphics.OrderedGroup(3), self.camera) self.ui = graphics.OrderedGroup(2) self.space = Space() self.space.gravity = (0.0, 0.0) buffer = 100 borders = Body() borders.position = (0, 0) left = Segment(borders, (-buffer, -buffer), (-buffer, self.world_size.height+buffer), buffer) bottom = Segment(borders, (-buffer, -buffer), (self.world_size.width+buffer, -buffer), buffer) right = Segment(borders, (self.world_size.width+buffer, self.world_size.height+buffer), (self.world_size.width+buffer, -buffer), buffer) top = Segment(borders, (self.world_size.width+buffer, self.world_size.height+buffer), (-buffer, self.world_size.height+buffer), buffer) self.space.add_static(left, bottom, right, top) self.stars = Stars(self.world_size, self.batch, self.background) self.asteroids = Asteroid.populate(50, 100, self.world_size, self.batch, self.foreground, self.space) if not self.asteroids: print("None of a particular resource on this asteroid belt, that'd be unfair. Trying again.") self.end(Main()) return self.home_world = choice([asteroid for asteroid in self.asteroids if asteroid.position.y > self.world_size.height/4*3]) self.home_world.type = "home" self.home_world.populated = True x, y = self.home_world.position self.camera.move(Vector(x-self.size.width/2, y-self.size.height/2)) # Let's make stuff a bit more interesting. for asteroid in self.asteroids: if not asteroid.type == "home": asteroid.body.apply_impulse((triangular(-20000, 20000, 0), triangular(-20000, 20000, 0))) x, y = self.home_world.position self.player = Person(x+150, y+150, self.batch, self.playerg, self.space) self.mouse = x+150, y+150 centre = Vector(self.size.width/2, self.size.height/2) image = centre_image(resource.image("logo.png")) self.logo = sprite.Sprite(image, centre.x, centre.y, batch=self.batch, group=self.ui) self.logo.opacity = 255 self.fade = True self.faded = False planet = centre_image(resource.image("planet.png")) x = self.world_size.width/2 y = planet.height/2 self.planet_sprite = sprite.Sprite(planet, x, y, batch=self.batch, group=self.world_ui) self.win_box = BB(x-200, y-200, x+200, y+200) #self.tools = sorted([tool(self.space) for tool in Tool.__subclasses__()], key=attrgetter("order"), reverse=True) #self.buttons = {tool: Button(30, 30+number*50, tool.image, tool.description, self.use_tool(tool), self.ui, self.batch) for number, tool in enumerate(self.tools)} self.constraints = set()
class Main(Scene): FADE_SPEED = 75 def load(self): self.world_size = Size(3000, 3000) self.camera = Camera(self.size, self.world_size, 1000, 10) self._tool = None self.tool = None self.batch = graphics.Batch() self.background = CameraGroup(graphics.OrderedGroup(0), self.camera) self.foreground = CameraGroup(graphics.OrderedGroup(1), self.camera) self.playerg = CameraGroup(graphics.OrderedGroup(2), self.camera) self.world_ui = CameraGroup(graphics.OrderedGroup(3), self.camera) self.ui = graphics.OrderedGroup(2) self.space = Space() self.space.gravity = (0.0, 0.0) buffer = 100 borders = Body() borders.position = (0, 0) left = Segment(borders, (-buffer, -buffer), (-buffer, self.world_size.height+buffer), buffer) bottom = Segment(borders, (-buffer, -buffer), (self.world_size.width+buffer, -buffer), buffer) right = Segment(borders, (self.world_size.width+buffer, self.world_size.height+buffer), (self.world_size.width+buffer, -buffer), buffer) top = Segment(borders, (self.world_size.width+buffer, self.world_size.height+buffer), (-buffer, self.world_size.height+buffer), buffer) self.space.add_static(left, bottom, right, top) self.stars = Stars(self.world_size, self.batch, self.background) self.asteroids = Asteroid.populate(50, 100, self.world_size, self.batch, self.foreground, self.space) if not self.asteroids: print("None of a particular resource on this asteroid belt, that'd be unfair. Trying again.") self.end(Main()) return self.home_world = choice([asteroid for asteroid in self.asteroids if asteroid.position.y > self.world_size.height/4*3]) self.home_world.type = "home" self.home_world.populated = True x, y = self.home_world.position self.camera.move(Vector(x-self.size.width/2, y-self.size.height/2)) # Let's make stuff a bit more interesting. for asteroid in self.asteroids: if not asteroid.type == "home": asteroid.body.apply_impulse((triangular(-20000, 20000, 0), triangular(-20000, 20000, 0))) x, y = self.home_world.position self.player = Person(x+150, y+150, self.batch, self.playerg, self.space) self.mouse = x+150, y+150 centre = Vector(self.size.width/2, self.size.height/2) image = centre_image(resource.image("logo.png")) self.logo = sprite.Sprite(image, centre.x, centre.y, batch=self.batch, group=self.ui) self.logo.opacity = 255 self.fade = True self.faded = False planet = centre_image(resource.image("planet.png")) x = self.world_size.width/2 y = planet.height/2 self.planet_sprite = sprite.Sprite(planet, x, y, batch=self.batch, group=self.world_ui) self.win_box = BB(x-200, y-200, x+200, y+200) #self.tools = sorted([tool(self.space) for tool in Tool.__subclasses__()], key=attrgetter("order"), reverse=True) #self.buttons = {tool: Button(30, 30+number*50, tool.image, tool.description, self.use_tool(tool), self.ui, self.batch) for number, tool in enumerate(self.tools)} self.constraints = set() def use_tool(self, tool): """For callback usage.""" def f(): self.tool = tool return f @property def tool(self): return self._tool @tool.setter def tool(self, tool): if tool: if self._tool and not self._tool == tool: self._tool.end_selecting() self.buttons[self._tool].normal() self.window.set_mouse_cursor(self.window.get_system_mouse_cursor(self.window.CURSOR_DEFAULT)) self.selecting = False self.selection = [] self.window.set_mouse_cursor(self.window.get_system_mouse_cursor(self.window.CURSOR_CROSSHAIR)) self.selecting = True self.buttons[tool].using() else: if self._tool: self._tool.end_selecting() self.buttons[self._tool].normal() self.window.set_mouse_cursor(self.window.get_system_mouse_cursor(self.window.CURSOR_DEFAULT)) self.selecting = False self.selection = [] self._tool = tool def key_pressed(self, symbol, modifiers): self.fade = True #self.camera.key_pressed(symbol) def key_released(self, symbol, modifiers): pass #self.camera.key_released(symbol) def mouse_pressed(self, x, y, key, modifiers): self.fade = True #for button in self.buttons.values(): # if button.point_over(x, y): # button.callback() # return if self.selecting: for asteroid in self.asteroids: clicked = self.camera.translate(x, y) if asteroid.point_over(*clicked): self.selection.append((asteroid, clicked)) self.tool = self.tool.selection(self.selection, self.constraints) return self.tool = None return def mouse_motion(self, x, y, dx, dy): self.mouse = self.camera.translate(x, y) #for button in self.buttons.values(): # if button.point_over(x, y): # button.on_mouse_over() # else: # button.on_mouse_leave() def mouse_drag(self, x, y, dx, dy, buttons, modifiers): if buttons & window.mouse.RIGHT: self.camera.mouse_dragged(dx, dy) def update(self, frame_time): self.constraints = {constraint for constraint in self.constraints if not constraint.update(self.batch, self.foreground)} if self.fade and not self.faded: self.logo.opacity -= Main.FADE_SPEED*frame_time if self.logo.opacity < 0: self.logo.opacity = 0 del self.logo self.faded = True self.player.target = self.mouse self.player.update() x, y = self.player.body.position self.camera.x, self.camera.y = x-self.size.width/2, y-self.size.height/2 self.camera.update(frame_time) self.space.step(1/60) if self.win_box.contains_vect(self.player.body.position): self.end(Win()) def draw(self): self.batch.draw()
import objects import inputcontrol import loading from graphics import Camera from pygame.math import Vector2 import sys from pygame.locals import * if __name__ == "__main__": pygame.init() display = pygame.display.set_mode((960, 540)) pygame.display.set_caption("Pygame Level Editor") clock = pygame.time.Clock() camera = Camera(15) while True: for event in pygame.event.get(): if event.type == QUIT: import ctypes if ctypes.windll.user32.MessageBoxW( 0, "Any unsaved progress will be lost!", "Are you sure?", 1) == 1: print("Editor exit") pygame.quit() sys.exit() elif event.type == MOUSEMOTION: pygame.display.update() clock.tick(30)