def hit_detection(G=G): #platform hit detection plats = [Rect((x, y), (w, h)) for x, y, w, h, idx in G[LVL][PLATS]] off, box = hitbox_data[G[STATE]] hitbox = Rect((G[X]+off[0], G[Y]+off[1]), box) if G[X_VEL]: xflag = abs(G[X_VEL]) > 0 while hitbox.move(G[X_VEL], 0).collidelist(plats) != -1: G[X_VEL] += 1 if G[X_VEL] < 0 else -1 if xflag and not G[X_VEL]: if G[STATE] in ["DIVE", "DIVELANDJUMP"]: G[STATE] = "BONK" G[FRAME] = 0 if G[Y_VEL]: yflag = G[Y_VEL] > 0 while hitbox.move(0, G[Y_VEL]).collidelist(plats) != -1: G[Y_VEL] += 1 if G[Y_VEL] < 0 else -1 if yflag and not G[Y_VEL]: # i dont love changing the state here but it seems fine if G[STATE] in ["FALLING", "AIR", "DIVELANDJUMP"]: G[STATE] = "LAND" G[FRAME] = 0 if G[STATE] in ["DIVE", "BONK"]: G[STATE] = G[STATE] + "LAND" G[FRAME] = 0 if G[X_VEL] and G[Y_VEL]: while hitbox.move(G[X_VEL], G[Y_VEL]).collidelist(plats) != -1: G[X_VEL] += 1 if G[X_VEL] < 0 else -1 G[Y_VEL] += 1 if G[Y_VEL] < 0 else -1
class Baffle(pygame.sprite.Sprite): def __init__(self, screecSize): super().__init__() self.xlim = screecSize[0] self.ylim = screecSize[1] self.image = pygame.image.load(r'./Image/Baffle.png').convert_alpha() self.image = transform.scale(self.image, (100, 15)) self.rect = Rect(self.xlim // 2 - 50, self.ylim - 15 - 5, 100, 15) self.speed = 200 def update(self, dt): keys = pygame.key.get_pressed() dx = dt * self.speed if keys[pygame.K_RIGHT]: self.moveRight(dx) if keys[pygame.K_LEFT]: self.moveLeft(dx) def moveRight(self, dx): toRight = self.xlim - self.rect.right if toRight <= 0: return elif toRight < dx: self.rect = self.rect.move(toRight, 0) else: self.rect = self.rect.move(dx, 0) def moveLeft(self, dx): toLeft = self.rect.left if toLeft <= 0: return elif toLeft < dx: self.rect = self.rect.move(-toLeft, 0) else: self.rect = self.rect.move(-dx, 0)
class GameObject: def __init__(self, x, y, w, h, speed=(0, 0)): self._rect = Rect(x, y, w, h) self._speed = (round(speed[0] * c.speed_scale), round(speed[1] * c.speed_scale)) @property def rect(self): return self._rect @property def speed(self): return self._speed def draw(self, surface): pass def move(self, dx, dy): self._rect = self._rect.move(dx, dy) def update(self): """""" if self._speed == [0, 0]: return self.move(*self._speed)
class Asteroid(Sprite): def __init__(self, img_name, width, height, game): super().__init__() self.image = pygame.image.load(img_name).convert_alpha() self.rect = Rect(randrange(game.width - width), -100, width, height) self.y_speed = randrange(15, 25) self.game = game def update(self, *args): x_move = 0 y_move = self.y_speed self.rect = self.rect.move(x_move, y_move) if self.rect.top > self.game.height: super().kill() def kill(self): self.game.elements['exploding_asteroids'].add(AnimatedAsteroid(join('gfx', 'asteroid_exploded.png'), self.rect, 4, self.game)) if self.game.newPU: self.game.elements['power-ups'].add(PowerUp(join('gfx', 'PowerUp.png'), self.rect, self.game)) self.game.newPU = False super().kill()
def draw_control(self): top_left_rect = Rect( 0, 0, self.dimensions.width - self.padding, self.dimensions.height - self.padding ) bottom_right_rect = Rect( self.padding, self.padding, self.dimensions.width - self.padding, self.dimensions.height - self.padding ) label = self.font.render(self.text, 1, color(255, 255, 255, 255)) font_rect = Rect( (self.dimensions.width / 2) - (label.get_width() / 2), (self.dimensions.height / 2) - (label.get_height() / 2), self.dimensions.width, self.dimensions.height ) if self.mouse_entered and self.left_down: font_rect = font_rect.move(self.padding / 2, self.padding / 2) self.surface.fill(color(0, 0, 200, 255), rect=bottom_right_rect) self.surface.fill(color(0, 0, 75, 255), rect=top_left_rect) self.surface.fill(color(0, 0, 250, 255), rect=Rect(self.padding, self.padding, self.dimensions.width - self.padding * 2, self.dimensions.height - self.padding * 2)) else: self.surface.fill(color(0, 0, 75, 255), rect=bottom_right_rect) self.surface.fill(color(0, 0, 200, 255), rect=top_left_rect) self.surface.fill(color(0, 0, 250, 255), rect=Rect(self.padding, self.padding, self.dimensions.width - self.padding * 2, self.dimensions.height - self.padding * 2)) self.surface.blit(label, font_rect)
class GameObject: def __init__(self, x, y, w, h, speed=[0, 0]): self.bounds = Rect(x, y, w, h) self.speed = speed.copy() self.old_pos = None self.old_speed = None @property def left(self): return self.bounds.left @property def right(self): return self.bounds.right @property def top(self): return self.bounds.top @property def bottom(self): return self.bounds.bottom @property def width(self): return self.bounds.width @property def height(self): return self.bounds.height @property def center(self): return self.bounds.center @property def centerx(self): return self.bounds.centerx @property def centery(self): return self.bounds.centery def draw(self, surface): pass def move(self, dx: object, dy: object) -> object: self.bounds = self.bounds.move(dx, dy) def update(self): """""" self.old_pos = self.bounds.copy() self.old_speed = self.speed.copy() if self.speed == [0, 0]: return self.move(*self.speed)
class LaserSprite(Sprite): def __init__(self, img_name, rect, game): self.image = pygame.image.load(img_name).convert_alpha() self.rect = Rect(rect.centerx, rect.y, 2, 9) super(LaserSprite, self).__init__() def update(self, *args): super(LaserSprite, self).update() self.rect = self.rect.move(0, -20)
def animate(self): s = pygame.Surface(self.size) height = self.size[1] self.offset += 0.1 for step in self.steps: src = Rect(step, 0, 2, height) dst = src.move(0, math.cos(self.offset + step * .02) * self.amount) s.blit(self.base, dst, src) return s
class LaserSprite(Sprite): def __init__(self, img_name, rect): self.image = pygame.image.load(img_name).convert_alpha() self.rect = Rect(rect.centerx, rect.y, 2, 9) super().__init__() def update(self): self.rect = self.rect.move(0, -10) if self.rect.bottom < 0: self.kill()
class GameObject: def __init__(self, x, y, w, h, speed=(0, 0)): self.bounds = Rect(x, y, w, h) self.speed = speed @property def left(self): return self.bounds.left @property def right(self): return self.bounds.right @property def top(self): return self.bounds.top @property def bottom(self): return self.bounds.bottom @property def width(self): return self.bounds.width @property def height(self): return self.bounds.height @property def center(self): return self.bounds.center @property def centerx(self): return self.bounds.centerx @property def centery(self): return self.bounds.centery def draw(self, surface): pass def move(self, dx, dy): # dx = ... # dy = ... self.bounds = self.bounds.move(dx, dy) def update(self): if self.speed == [0, 0]: return self.move(*self.speed)
class Camera(pygame.sprite.Group): """Camera class. Follow the player on the screen""" # Position of the camera is the top left corner def __init__(self, nmbrOfTilesOnScreen, tileSize, screenSize): """Basic camera information""" pygame.sprite.Group.__init__(self) self.tileSize = tileSize self.nmbrOfTilesOnScreen = nmbrOfTilesOnScreen self.screenRect = Rect(0, 0, screenSize.x, screenSize.y) self.tileRect = Rect(0, 0, nmbrOfTilesOnScreen.x, nmbrOfTilesOnScreen.y) self.levelSize = Vector(0, 0) def setPosition(self, x, y): """Set the position of the camera""" x = x - self.tileRect.x - self.tileRect.width / 2 y = y - self.tileRect.y - self.tileRect.height / 2 self.tileRect = self.tileRect.move(x, y) def setVisibleSprites(self, level): """Get the tiles that fall inside the screen and show them""" tiles = level.getTilesInRect(self.tileRect, self.tileRect) self.levelSize = Vector(level.width, level.height) self.empty() self.add(tiles) def MoveCameraToPlayerLocation(self, player): """Move the camera so that it is centered on the player but doesn't go past the side""" self.screenRect.x = max(player.x - self.screenRect.width / 2, 0) self.screenRect.x = min(max(0, (self.levelSize.x * self.tileSize.x - self.screenRect.width)), self.screenRect.x) self.screenRect.y = max(player.y - self.screenRect.height / 2, 0) self.screenRect.y = min(max(0, (self.levelSize.y * self.tileSize.y - self.screenRect.height)), self.screenRect.y) def draw(self, surface, player, npcList): """Draw the player, creatures and tiles with the camera movement""" self.MoveCameraToPlayerLocation(player.rect) sprites = self.sprites() surface_blit = surface.blit # Move every sprite so as to be well placed with the camera for spr in sprites: rect = Rect(spr.rect.x - self.screenRect.x, spr.rect.y - self.screenRect.y, 0, 0) self.spritedict[spr] = surface_blit(spr.image, rect) for npc in npcList: rect = Rect(npc.rect.x - self.screenRect.x, npc.rect.y - self.screenRect.y, 0, 0) surface_blit(npc.image, rect) rect = Rect(player.rect) rect.x -= self.screenRect.x rect.y -= self.screenRect.y player.net.draw(surface_blit, self.screenRect) surface_blit(player.image, rect) self.lostsprites = []
class GameObject: def __init__(self, x, y, w, h, speed=(0, 0)): self.bounds = Rect(x, y, w, h) self.speed = speed #@property - объект вычисляемого свойства только для чтения #по факту я определяю сам объект (его границы) и возможность перемещения @property def left(self): return self.bounds.left @property def right(self): return self.bounds.right @property def top(self): return self.bounds.top @property def bottom(self): return self.bounds.bottom @property def width(self): return self.bounds.width @property def height(self): return self.bounds.height @property def center(self): return self.bounds.center @property def centerx(self): return self.bounds.centerx @property def centery(self): return self.bounds.centery def draw(self, surface): pass def move(self, dx, dy): self.bounds = self.bounds.move(dx, dy) def update(self): if self.speed == [0, 0]: return self.move(*self.speed)
class GameObject: def __init__(self, x: int, y: int, width: int, height: int, speed=(0, 0)): self.bounds = Rect(x, y, width, height) self.speed = speed @property def left(self): return self.bounds.left @property def right(self): return self.bounds.right @property def width(self): return self.bounds.width @property def height(self): return self.bounds.height @property def top(self): return self.bounds.top @property def bottom(self): return self.bounds.bottom @property def center(self): return self.bounds.center @property def centerx(self): return self.bounds.centerx @property def centery(self): return self.bounds.centery def draw(self, surface) -> None: pass def move(self, dx, dy) -> None: self.bounds = self.bounds.move(dx, dy) def update(self) -> None: if self.speed == [0, 0]: return self.move(*self.speed)
class Ship(Sprite): def __init__(self, img_name, width, height, game): super().__init__() self.image = pygame.image.load(img_name).convert_alpha() self.rect = Rect(454, 516, width, height) self.exploded = False self.game = game self.cannon_cooldown = 15 self.poweruptime = 0 def update(self): if not self.exploded: if self.game.ship_collides: self.exploded = True return x_move = 0 y_move = 0 if self.game.input.up and not self.rect.top <= 0: y_move -= 20 elif self.game.input.down and not self.rect.bottom >= self.game.height: y_move += 20 if self.game.input.left and not self.rect.left <= 0: x_move -= 20 if self.game.input.right and not self.rect.right >= self.game.width: x_move += 20 if self.game.input.fire: if self.poweruptime > 0: self.game.laser_sound.play() self.game.elements['lasers'].add(LaserSprite(join('gfx', 'laser.png'), self.rect)) self.poweruptime -= 1 if not self.game.ship_catches: if not self.cannon_cooldown: self.game.laser_sound.play() self.game.elements['lasers'].add( LaserSprite(join('gfx', 'laser.png'), self.rect)) self.cannon_cooldown = 6 else: self.game.ship_catches.pop() self.poweruptime += 75 self.rect = self.rect.move(x_move, y_move) self.cannon_cooldown = self.cannon_cooldown - 1 if self.cannon_cooldown else 0 else: self.groups()[-1].add(AnimatedShip(join('gfx', 'ship_exploded.png'), self.rect, 3, self.game))
class GameObject: def __init__(self, x, y, w, h, speed=(0, 0)): self.bounds = Rect(x, y, w, h) self.speed = speed @property def center(self): return self.bounds.center def move(self, dx, dy): self.bounds = self.bounds.move(dx, dy) def update(self): if self.speed == (0, 0): return self.move(*self.speed)
class GameObject: def __init__(self, x, y, w, h): self.bound = Rect(x, y, w, h) @property def left(self): return self.bound.left @property def right(self): return self.bound.right @property def top(self): return self.bound.top @property def bottom(self): return self.bound.bottom @property def width(self): return self.bound.width @property def height(self): return self.bound.height @property def center(self): return self.bound.center @property def centerx(self): return self.bound.centerx @property def centery(self): return self.bound.centery def move(self, dx, dy): self.bound = self.bound.move(dx, dy) def draw(self, surface): pass
class GameObject: def __init__(self, x, y, w, h, speed=(0, 0)): self.x = x self.y = y self.bounds = Rect(x, y, w, h) self.speed = speed def draw(self, surface): #отрисовать pass def move(self, dx, dy): # self.bounds = self.bounds.move(dx, dy) def update(self): #обновить координаты if self.speed == [0, 0]: return self.move(*self.speed)
class PowerUp(Sprite): def __init__(self, img_name, rect, game): self.game = game self.image = pygame.image.load(img_name).convert_alpha() self.rect = Rect(rect.centerx, rect.y, 40, 40) self.x = choice([-5, 5]) self.y = choice([-5, 5]) super().__init__() def update(self): if self.rect.top <= 0: self.y = 5 if self.rect.bottom >= self.game.height: self.y = -5 if self.rect.left <= 0: self.x = 5 if self.rect.right >= self.game.width: self.x = -5 self.rect = self.rect.move(self.x, self.y)
class Feature: def __init__(self, x, y, w, h, image): self.init_x = x self.init_y = y self.i_x = 0 self.i_y = 0 self.p = 0 self.init_bounds = Rect(x, y, w, h) self.bounds = Rect(x, y, w, h) self.image = image self.percents = 30 self.percents2 = 30 def draw(self, surface): surface.blit(self.image, self.bounds) def move(self, x, y, speed=None): # print("start x =", x) # print("start y =", y) new_x = x #+ self.bounds.w * (100 - self.percents) / 100 / 2 new_y = y #+ self.bounds.h * (100 - self.percents) / 100 / 2 # print("end x =", new_x) # print("end y =", new_y) self.bounds = self.bounds.move( new_x, new_y) #двигает прямоуголник в новые координаты def scale(self, percents, image): self.image = image self.image = pygame.transform.scale( self.image, (int(self.init_bounds.w * 0.01 * percents), int(self.init_bounds.h * 0.01 * percents))) self.percents = percents pygame.time.wait(10) self.bounds.x += self.bounds.w * (100 - self.percents) / 100 / 2 self.bounds.y += self.bounds.h * (100 - self.percents) / 100 / 2 self.bounds.x += self.bounds.w * (self.percents2 - percents) / 100 / 2 self.bounds.y += self.bounds.h * (self.percents2 - percents) / 100 / 2 self.percents2 = percents
class Asteroid(Sprite): def __init__(self, img_name, width, height, game): super(Asteroid, self).__init__() self.image = pygame.image.load(img_name).convert_alpha() self.rect = Rect(randrange(956 - width), -100, width, height) self.y_speed = randrange(10, 30) self.game = game def update(self, *args): x_move = 0 y_move = self.y_speed self.rect = self.rect.move(x_move, y_move) def kill(self): self.game.elements['exploding_asteroids'].add( AnimatedAsteroid(join('gfx', 'asteroid_exploded.png'), self.rect, 4, self.game)) super(Asteroid, self).kill()
class Ship(Sprite): def __init__(self, img_name, width, height, game): super(Ship, self).__init__() self.image = pygame.image.load(img_name).convert_alpha() self.rect = Rect(454, 516, width, height) self.exploded = False self.game = game self.cannon_cooldown = 10 def update(self): if not self.exploded: if self.game.ship_collides: self.exploded = True return x_move = 0 y_move = 0 if self.game.input.up_pressed: y_move -= 20 elif self.game.input.down_pressed: y_move += 20 if self.game.input.left_pressed: x_move -= 20 if self.game.input.right_pressed: x_move += 20 if self.game.input.space_pressed: if not self.cannon_cooldown: self.game.laser_sound.play() self.game.elements['lasers'].add( LaserSprite(join('gfx', 'laser.png'), self.rect, self.game)) self.cannon_cooldown = 6 self.rect = self.rect.move(x_move, y_move) self.cannon_cooldown = self.cannon_cooldown - 1 if self.cannon_cooldown else 0 else: self.groups()[-1].add( AnimatedShip(join('gfx', 'ship_exploded.png'), self.rect, 3, self.game))
def draw(self, screen): super().draw(screen) mark_rect = Rect(self.absolute_bounds.x, self.absolute_bounds.y, self.mark_size, self.mark_size) for unit in self.game.sprites: shape = unit.cls_dict.get('mark_shape', 'quad') pos = self.worldpos_to_minimap(unit.pos.x, unit.pos.y) mark_rect.centerx = self.absolute_bounds.x + pos[0] mark_rect.centery = self.absolute_bounds.y + pos[1] if shape == 'circle': pygame.draw.ellipse(screen, self.mark_color, mark_rect) pygame.draw.ellipse(screen, unit.team_color, mark_rect, 2) if shape == 'quad': pygame.draw.rect(screen, self.mark_color, mark_rect) pygame.draw.rect(screen, unit.team_color, mark_rect, 3) camera_rect = Rect( (self.game.world_size - self.game.camera.offset_x) * self.world_ratio_width, (self.game.world_size - self.game.camera.offset_y) * self.world_ratio_height, config['screen']['size'][0] * self.world_ratio_width, config['screen']['size'][1] * self.world_ratio_height ) pygame.draw.rect(screen, Color('yellow'), camera_rect.move(self.absolute_bounds.x, self.absolute_bounds.y), 1)
class GameEntity: def __init__(self, x, y, speed=(0, 0), fps=DEFAULT_FPS): self.fps = fps self.speed = speed self.bounds = Rect(x, y, 50, 50) self.bounds.center = (x, y) self.direction = TO_THE_RIGHT self.image = load_image('monster01.png', -1) self.image_dx = (self.bounds.w - self.image.get_width()) // 2 self.image_dy = (self.bounds.h // 2 - self.image.get_height()) def draw(self, surface, camera): surface.blit(self.image, (self.bounds.x + self.image_dx - camera.x, self.bounds.y + self.image_dy - camera.y)) def get_rect_image(self): return self.image.get_rect().move( (self.bounds.x + self.image_dx, self.bounds.y + self.image_dy)) def update(self, target): # dx = 0 # dy = 0 line_speed = PLAYER_SPEED // 4 // self.fps if self.bounds.centerx - target.x > 0: dx = -line_speed else: dx = line_speed if self.bounds.centery - target.y > 0: dy = -line_speed else: dy = line_speed self.speed = (dx, dy) self.move(*self.speed) def move(self, dx, dy): self.bounds = self.bounds.move(int(dx), int(dy))
class Widget(Sprite): """Use Widget class for better movement tracking Widget class inherits from Sprite class. Test cases for class Widget >>> from widget import * >>> import pygame >>> s = pygame.surface.Surface((30,30)) >>> w = Widget(s, (0,0,30,30), (0,0)) >>> w.rect <rect(0, 0, 30, 30)> >>> w.update() >>> w.rect <rect(0, 0, 30, 30)> >>> w.getMovement() [0, 0] >>> w.setX(1) >>> w.getX() 1 >>> w.setY(4) >>> w.getY() 4 >>> w.setMovement((3,5)) >>> w.getMovement() (3, 5) >>> w.getName() (0, 0) >>> w.setPosition((5,7)) >>> w.getPosition() (5, 7) """ def __init__(self, image, rect, name=''): """Instantiate a widget with a given surface, rectangle, and (x,y) movement pair. """ Sprite.__init__(self) self.movement = [0, 0] self.rect = Rect(rect) self.lastRect = self.rect self.image = image self.name = name self.frames = [] self.frameIndex = 0 self.frameRate = 1 self.timeDelay = WIDGETFRAMES self.lastUpdate = 0 self.world = None self.undone = False self.id = self.rect.top + self.rect.left +\ self.rect.width + self.rect.height def attachToWorld(self, world): self.world = world self.id = self.world.curWidgetID self.world.curWidgetID += 1 def startAnimation(self, frames, startIndex, frameRate): self.frames = frames self.frameIndex = startIndex self.frameRate = frameRate self.image = self.frames[startIndex] self.lastUpdate = self.timeDelay def __str__(self): return str(self.rect.left) + str(self.rect.top) + str(self.id) def setMovement(self, vector): """Set movement with a pair""" if (self.movement != [0, 0] and vector == [0, 0]): self.world.dirtyGroup.add(self) self.movement = vector def getMovement(self): """Return movement as a pair""" return self.movement def setStop(self): """Set movement to 0""" self.setMovement([0, 0]) def setY(self, y): """Set y-component of movement""" self.movement[1] = y def setX(self, x): """Set x-component of movement""" self.movement[0] = x def getX(self): """Get x-component of movement""" return self.movement[0] def getY(self): """Set y-component of movement""" return self.movement[1] def setPosition(self, pair): """Set x and y coords of Widget""" self.rect.topleft = pair def getPosition(self): """Get x and y coords of Widget""" return self.rect.topleft def update(self): """Move sprite according to its movement vector""" # Widget needs to be animated if (len(self.frames) > 0): if self.lastUpdate <= 0: self.frameIndex = (self.frameIndex + 1) % (len(self.frames)) self.image = self.frames[self.frameIndex] self.lastUpdate = self.timeDelay self.world.dirtyGroup.add(self) else: self.lastUpdate -= 1 elif (self.getMovement != [0, 0]): self.lastRect = Rect(self.rect) self.rect.move_ip(self.movement) self.world.dirtyGroup.add(self) def undoUpdate(self): """Widget returns to state prior to last update()""" self.rect = self.lastRect def getShadow(self): shadow = Sprite() shadow.rect = self.lastRect.move(0, 0) return shadow def getName(self): """Get name of Widget""" return self.name
pygame.init() screen = pygame.display.set_mode(size) color = (255, 255, 255) screen.fill(color) pygame.display.update() dino = pygame.image.load("images/d1.png") dino_rect = Rect(50, 100, 0, 0) pygame.display.update() done = 0 dino_rect = Rect(50, 100, 0, 0) clock = Clock() # 设置时钟 while not done: clock.tick(10) for e in pygame.event.get(): if e.type == pygame.QUIT: done = 1 break if dino_rect.left >= size[0] - dino.get_width(): print("撞墙了") else: dino_rect = dino_rect.move([10, 0]) # 每次往前移动10 screen.fill(color) # 一定要重新填充背景色,否则移动时会留下上次的影子 screen.blit(dino, dino_rect) pygame.display.update() pygame.quit()
class Graphic(Component): """A 2D image that can draw itself onto a Surface or another Graphic. It maintains a position relative to the associated Entity's on-screen position and will be drawn there accordingly. (For example, if the Entity's position is (30, 30) and this Graphic's position is (2, 3), it will be drawn to (32, 33) on-screen.) Several effects can also be applied to it, such as flipping the image and adding or reducing transparency. Attributes: _image (Surface): Contains the Graphic's actual pixel data. _rect (Rect): Contains the Graphic's x and y-offsets relative to its associated Entity, as well as its width and height. """ def __init__(self, source, x=0, y=0): """Declare and initialize instance variables. Args: source (Surface): Contains the 2D image associated with this Graphic. x (int): The x-offset of the Graphic's top-left corner relative to its associated Entity. The default value is 0. y (int): The y-offset of the Graphic's top-left corner relative to its associated Entity. The default value is 0. """ super(Graphic, self).__init__() self._image = convert_to_colorkey_alpha(source) self._rect = Rect(x, y, source.get_width(), source.get_height()) def offset(self, dx=0, dy=0): """Move the Graphic away from its original position relative to the associated Entity by a set horizontal and/or vertical distance. It is safe to pass floats as arguments to this method; they will automatically be rounded to the nearest whole number. Args: dx (int): The horizontal distance to travel. A positive value will move the Graphic to the right, while a negative value will move it to the left. Defaults to 0. dy (int): The vertical distance to travel. A positive value will move the Graphic down, while a negative value will move it up. Defaults to 0. """ self._rect.move_ip(int(round(dx)), int(round(dy))) def set_position(self, new_x=None, new_y=None): """Re-position the Graphic onto an exact location relative to its associated Entity. It is safe to pass floats as arguments to this method; they will automatically be rounded to the nearest whole number. Args: new_x (int): The x-coordinate of the top-left corner for the Graphic's new position. This parameter is optional; you can omit it from the function call if you want to retain the Graphic's x-position. new_y (int): The y-coordinate of the top-left corner for the Graphic's new position. This parameter is optional; you can omit it from the function call if you want to retain the Graphic's y-position. """ if new_x is not None: self._rect.x = int(round(new_x)) if new_y is not None: self._rect.y = int(round(new_y)) def get_width(self): return self._rect.width def get_height(self): return self._rect.height def center(self, axis, container_rect): """Move the associated Entity so that this Graphic is centered horizontally and/or vertically within an area of the screen. Args: axis (Axis): A literal from the Axis enum for specifying whether the image should be centered on the horizontal or vertical plane. To center the image on both planes, you can combine both values using the | (bitwise or) operator. container_rect (Rect): A Rect containing the position and dimensions of the area that this Graphic will be centered relative to. """ if (axis & Axis.horizontal) == Axis.horizontal: centered_x = (container_rect.width - self.get_width()) / 2 centered_x += container_rect.x self.entity.set_position(new_x=centered_x - self._rect.x) if (axis & Axis.vertical) == Axis.vertical: centered_y = (container_rect.height - self.get_height()) / 2 centered_y += container_rect.y self.entity.set_position(new_y=centered_y - self._rect.y) def is_contained(self, container_rect): """Return a Boolean indicating whether this Graphic is completely contained within an area of the screen. (i.e. No pixels exceed the area's boundaries.) Args: container_rect (Rect): Contains the position and dimensions of the area this Graphic will be compared to. """ if container_rect.contains(self.draw_rect()): return True else: return False def is_outside(self, container_rect): """Return a Boolean indicating whether this Graphic is completely outside of an area of the screen. (i.e. All pixels exceed the area's boundaries.) Args: container_rect (Rect): Contains the position and dimensions of the area this Graphic will be compared to. """ if not container_rect.colliderect(self.draw_rect()): return True else: return False def draw_rect(self): """Return a Rect containing the actual position the Graphic will be drawn to, with the Entity's position taken into account. """ return self._rect.move(self.entity.x, self.entity.y) def flip(self, axis): """Flip the image horizontally and/or vertically. Args: axis (Axis): A literal from the Axis enum for specifying whether to apply a horizontal or vertical flip. To flip the image both ways, you can combine both values using the | (bitwise or) operator. """ if (axis & Axis.horizontal) == Axis.horizontal: self._image = pygame.transform.flip(self._image, True, False) if (axis & Axis.vertical) == Axis.vertical: self._image = pygame.transform.flip(self._image, False, True) def magnify(self, zoom): """Enlarge or shrink the image using an equal scale for the width and height. Args: zoom (float): The amount used to multiply the image's dimensions. For example, passing a value of 2 when the image's dimensions are 24x24 will enlarge the image to 48x48. Passing 0.5 will shrink it to 12x12. """ magnified_image = pygame.transform.scale( self._image, (int(round(self.get_width() * zoom)), int(round(self.get_height() * zoom)))) self._image = magnified_image self._update_rect_dimensions() def resize(self, new_width, new_height): """Stretch and/or shrink the image to fit new dimensions. Args: new_width (int): The width that the image will shrink or stretch to fit. new_height (int): The height that the image will shrink or stretch to fit. """ resized_image = pygame.transform.scale( self._image, (int(round(new_width)), int(round(new_height)))) self._image = resized_image self._update_rect_dimensions() def _update_rect_dimensions(self): """Update the width and height of _rect with the current dimensions of _image. """ self._rect.width = self._image.get_width() self._rect.height = self._image.get_height() def opacify(self, amount): """Increase or decrease the image's transparency. Note that Graphic images always start with an opacity of 255, which is fully opaque. Args: amount (int): How much to add to the image's opacity value. Positive values will make the image more opaque, while negative values will make it more transparent. To make the image fully opaque, pass 255 or more. To make the image fully transparent, pass -255 or less. """ self._image.set_alpha(self._image.get_alpha() + amount) def is_opaque(self): """Return a Boolean indicating whether the image is fully opaque. """ if self._image.get_alpha() >= 255: return True else: return False def is_transparent(self): """Return a Boolean indicating whether the image is fully transparent. """ if self._image.get_alpha() <= 0: return True else: return False def blit(self, source, position, rect=None, special_flags=0): """Draw a Surface on top of this Graphic. Args: source (Surface): The image that will be drawn onto this Graphic. position (tuple of int, int): Contains the x and y-positions of the source image relative to this Graphic. area (Rect): An optional parameter specifying the region of the source image that will be used. Leave this parameter blank to draw the entire source image. special_flags (int): A combination of various PyGame flags for blitting effects. See the PyGame documentation on Surface.blit() for more information. This is an optional parameter; leave it blank to use no flags when blitting. Returns: A Rect containing the region of this Graphic that was drawn onto. """ x = position[0] y = position[1] return self._image.blit(source, (x, y), rect, special_flags) def draw(self, destination): """Draw this Graphic's image onto a destination Surface. Args: destination (Surface): Will have this Graphic drawn on it. Returns: A Rect containing the region of the destination that was drawn onto. """ return destination.blit(self._image, self.draw_rect())
class Unit(object): notable_attributes = {"Firepower", "Durability", "Velocity", "Rapidity"} walking_animations = 0 attacking_animations = 0 orientation_indices = (0, 0, 0, 0, 0, 0, 0, 0) cost = 0xA00 pace = 50 obstruance = grid.obstruance("unit") exclusion = grid.obstruance("notland") footprint = (10, 8) area_of_awareness = (50, 40) area_of_attack = (10, 8) is_flat = False aliment = None vital = False destination = None human = False def __init__(self, location): self.damage = 0 self.rect = Rect(0, 0, *self.footprint) self.rect.center = Rect(location).center self.animation_frame = 1 self.orient(6) self.image = self.obtain_frame() self.attacking = False self.walking = False self.finished = False self.temporal_accumulator = random.randint(0, 20) self.directions = Rect(0, 0, self.velocity, round(self.velocity * 0.8)) self.directions.center = (0, 0) self.reload_time = 0 self.flash = False self.killed_by = None def __getstate__(self): state = self.__dict__.copy() state["image"] = None return state def __setstate__(self, state): self.__dict__.update(state) self.image = self.obtain_frame() def step_position(self): """ where will it be on the next step? """ if self.walking: vector = octoclock_direction(self.orientation, self.directions) next_position = self.rect.move(vector) else: next_position = self.rect return next_position def orient(self, orientation): self.orientation = orientation self.attend_to_surroundings() def navigate(self, next_position): if grid.rect_in_bounds(next_position): self.move(next_position) def move(self, location): self.rect = location self.attend_to_surroundings() def attend_to_surroundings(self): """ arrange the areas of awareness and attack depending on the orientation as a number on the octoclock. 0 7 1 6 2 5 3 4 """ centre_of_attention = octoclock_direction(self.orientation, self.rect) self.rect_of_awareness = Rect(0, 0, *self.area_of_awareness) self.rect_of_awareness.center = centre_of_attention self.rect_of_attack = Rect(0, 0, *self.area_of_attack) self.attend_to_attack_area(centre_of_attention) def attend_to_attack_area(self, centre): """ Our modern military units have ranged attacks """ place_opposite(self.rect_of_attack, self.orientation, centre) def obtain_frame(self): """ obtain that portion of the animated chromograph that shows the unit as standing, walking or attacking in its current orientation """ frames_tall = max(self.orientation_indices) + 1 frames_wide = self.walking_animations + self.attacking_animations + 1 anim_row = self.orientation_indices[self.orientation] anim_col = self.animation_frame return chromographs.obtain_frame(self.animated_chromograph_name, anim_col, anim_row, frames_wide, frames_tall) def attack(self, audible=True): """ Assume a ferocius aspect """ if self.attacking or not self.attacking_animations: return if self.reload_time > time(): return self.temporal_accumulator = 0 self.attacking = True self.reload_time = time() + 5.0 / self.rapidity self.animation_frame = self.walking_animations + 1 self.image = self.obtain_frame() if audible: phonographs.play(self.attack_phonograph) return True def animate(self, ms): """ Create the illusion of movement """ self.temporal_accumulator += ms if self.temporal_accumulator < self.pace: return False # nothing to be done self.temporal_accumulator = 0 frame = self.animation_frame if self.destroyed(): frame = 0 elif self.attacking: frame += 1 if frame > self.walking_animations + self.attacking_animations: frame = 1 self.attacking = False elif self.walking: frame += 1 if frame > self.walking_animations: frame = 1 self.animation_frame = frame self.image = self.obtain_frame() return True # something was changed def harm(self, quanta_of_destruction, cause=None): """ Deal damage to the unit, possibly rendering it inactive """ self.damage += quanta_of_destruction self.flash = True if self.damage >= self.durability: self.obstruance = 0 self.animation_frame = 0 self.walking = False self.attacking = False self.image = self.obtain_frame() self.killed_by = cause def destroyed(self): return self.damage >= self.durability def things_perceived(self, things): """ things that are perceived """ indices = self.rect_of_awareness.collidelistall(things) return [things[i] for i in indices] def things_in_range(self, things): indices = self.rect_of_attack.collidelistall(things) def find_obstacles(self, location, knowledge): """ find things that obstruct a rectangle such that this unit may not occupy the same space if it were there. """ indices = location.collidelistall(knowledge) return [ knowledge[i] for i in indices if knowledge[i] is not self and (knowledge[i].obstruance & self.exclusion) ] def find_nearest(self, things): """ nearest thing sorted by distance from centre """ def dist(thing): rx, ry = thing.rect.center mx, my = self.rect.center return (rx - mx) ** 2 + (ry - my) ** 2 return sorted(things, key=dist) def orientation_towards(self, position): """ octaclock direction from self centre to position """ cx, cy = self.rect.center px, py = position[:2] dx = px - cx dy = py - cy steep = (dx == 0) or abs(dy / dx) > 3 shallow = (dy == 0) or abs(dx / dy) > 3 if steep: return 0 if dy < 0 else 4 if shallow: return 2 if dx > 0 else 6 if dy < 0: return 1 if dx > 0 else 7 return 3 if dx > 0 else 5
class Enemy(object): """ The enemy class. An instance of this class represents an enemy in the game. Attributes: _pos: The current position of the enemy. _direction: The current direction of the enemy. This could be LEFT or RIGHT. _collision_rect: The rectangle to check for collisions. _head_rect: The rectangle to check for deadly collisions. _walking_line: The rectangle to determine if the enemy is standing on the ground. _left_rect: The rectangle to determine if the enemy has reached the left end of a platform. _right_rect: The rectangle to determine if the enemy has reached the right end of a platform. _dy: The vertical speed of the enemy. _pic: The picture to display the enemy. _death_animation: The death animation of this enemy. _hit_sound: The sound when the character takes damage from this enemy. _death_sound: The sound when this enemy dies. """ # The horizontal speed. SPEED = 1 # Value for the _direction attribute. LEFT, RIGHT = range(2) # Maximal falling speed. MAX_FALLING = 2 # Vertical acceleration. V_FALLING = 0.1 def __init__(self, pos=None, direction=LEFT, pic=None, death_animation=None): if pos is None: pos = [0, 0] if pic is None or death_animation is None: animation_manager = AnimationManager.MANAGER animation = animation_manager.get_animation("animations") if pic is None: pic = Animated(PictureManager.MANAGER, animation[0], animation[1]["Monster"]) if death_animation is None: death_animation = (PictureManager.MANAGER, animation[0], animation[1]["MonsterExplode"]) self._pos = pos self._direction = direction self._collision_rect = Rect(pos, (31, 31)) self._head_rect = Rect((pos[0], pos[1] - 4), (31, 4)) self._walking_line = Rect((pos[0], pos[1] + 31), (31, 1)) self._left_rect = Rect((pos[0] - 1, pos[1] + 31), (1, 1)) self._right_rect = Rect((pos[0] + 30, pos[1] + 31), (1, 1)) self._dy = 0 self._pic = pic self._death_animation = death_animation self._hit_sound = SoundManager.MANAGER.get_sound("hit.wav") self._death_sound = SoundManager.MANAGER.get_sound("hit2.wav") def draw(self, surface, tick, camera, size): """ Draws the enemy. This method draws the enemy on the given surface. Args: surface: The surface to draw on. tick: the current tick of the game. camera: The position of the camera. size: The size of the window. This argument is not used at the moment. """ self._pic.draw(surface, self._collision_rect.x - camera[0], self._collision_rect.y - camera[1], tick, False) def tick(self, platforms): """ Method for handling the game ticks. This method should be called every tick to calculate the enemy changes. Args: platforms: The platforms of the level. """ if self._direction == Enemy.LEFT: dx = -Enemy.SPEED else: dx = Enemy.SPEED if self._dy < Enemy.MAX_FALLING: self._dy += Enemy.V_FALLING colliding = None for platform in platforms: if platform.collides(self._walking_line): colliding = platform break if colliding is not None: self._dy = 0 if not colliding.collides(self._left_rect): self._direction = Enemy.RIGHT elif not colliding.collides(self._right_rect): self._direction = Enemy.LEFT self._collision_rect = self._collision_rect.move(dx, self._dy) self._head_rect = self._head_rect.move(dx, self._dy) self._walking_line = self._walking_line.move(dx, self._dy) self._left_rect = self._left_rect.move(dx, self._dy) self._right_rect = self._right_rect.move(dx, self._dy) def collide(self, character): """ Checks for collision. This method checks for a collision with the character. If the character collides on the side he gets damage. If the character collides on the top the enemy dies and the character gets points. Args: character: The character to check with. Returns: The death animation if the enemy dies. None otherwise. """ if character.is_colliding(self._collision_rect): character.change_lives(-1) self._hit_sound.play() elif character.is_colliding(self._head_rect): character.change_points(10) character.jump() self._death_sound.play() return Animation(self._death_animation[0], self._death_animation[1], self._death_animation[2], (self._collision_rect.x - 14, self._collision_rect.y - 14)) return None
class GameObject: def __init__(self, x, y, w, h, speed=(0, 0)): #left, top, width, height self.bounds = Rect(x, y, w, h) self.speed = speed @property def left(self): return self.bounds.left @property def right(self): return self.bounds.right @property def top(self): return self.bounds.top @property def bottom(self): return self.bounds.bottom @property def width(self): return self.bounds.width @property def height(self): return self.bounds.height @property def center(self): return self.bounds.center @property def centerx(self): return self.bounds.centerx @property def centery(self): return self.bounds.centery def _left_egde(self): """ 충돌 검사를 위한 오브젝트의 왼쪽 면 """ return Rect(self.left, self.top, 1, self.height) def _right_egde(self): """ 충돌 검사를 위한 오브젝트의 오른쪽 면 """ return Rect(self.right, self.top, 1, self.height) def _top_egde(self): """ 충돌 검사를 위한 오브젝트의 위쪽 면 """ return Rect(self.left, self.top, self.width, 1) def _bottom_egde(self): """ 충돌 검사를 위한 오브젝트의 아랫쪽 면 """ return Rect(self.left, self.bottom, self.width, 1) @property def edges(self): return dict(left=self._left_egde(), right=self._right_egde(), top=self._top_egde(), bottom=self._bottom_egde()) def draw(self, surface): """ 자식 클래스에서 처리하도록 비워둠 """ pass def move(self, dx, dy): self.bounds = self.bounds.move(dx, dy) def update(self): if self.speed == [0, 0]: return #*연산자는 리스트를 튜플 형태로 전달 #**연산자는 딕셔너리 형태로 전달 self.move(*self.speed)
class Camera(pygame.sprite.Group): """Camera class. Follow the player on the screen""" # Position of the camera is the top left corner def __init__(self, nmbrOfTilesOnScreen, tileSize, screenSize): """Basic camera information""" pygame.sprite.Group.__init__(self) self.tileSize = tileSize self.nmbrOfTilesOnScreen = nmbrOfTilesOnScreen self.screenRect = Rect(0, 0, screenSize.x, screenSize.y) self.tileRect = Rect(0, 0, nmbrOfTilesOnScreen.x, nmbrOfTilesOnScreen.y) self.levelSize = Vector2(0, 0) self.fontRendererBig = pygame.font.Font(os.path.join("fonts", 'Millennium-Regular_0.ttf'), 18) self.fontRendererMedium = pygame.font.Font(os.path.join("fonts", 'Millennium-Regular_0.ttf'), 12) self.fontRendererSmall = pygame.font.Font(os.path.join("fonts", 'Millennium-Regular_0.ttf'), 8) def setPosition(self, x, y): """Set the position of the camera""" x = x - self.tileRect.x - self.tileRect.width / 2 y = y - self.tileRect.y - self.tileRect.height / 2 self.tileRect = self.tileRect.move(x, y) def setVisibleSprites(self, level): """Get the tiles that fall inside the screen and show them""" tiles = level.getTilesInRect(self.tileRect, self.tileRect) self.levelSize = Vector2(level.width, level.height) self.empty() self.add(tiles) def MoveCameraToPlayerLocation(self, player): """Move the camera so that it is centered on the player but doesn't go past the side""" self.screenRect.x = max(player.x - self.screenRect.width / 2, 0) self.screenRect.x = min(max(0, (self.levelSize.x * self.tileSize.x - self.screenRect.width)), self.screenRect.x) self.screenRect.y = max(player.y - self.screenRect.height / 2, 0) self.screenRect.y = min(max(0, (self.levelSize.y * self.tileSize.y - self.screenRect.height)), self.screenRect.y) def drawScreen(self, surface, level: Map, player, npcList): """Draw the player, creatures and tiles with the camera movement""" if player is not None: self.MoveCameraToPlayerLocation(player.rect) if level is not None: self.setVisibleSprites(level) sprites = self.sprites() surface_blit = surface.blit rect = Rect(0, 0, 0, 0) # Move every sprite so as to be well placed with the camera self.drawMap(surface_blit, sprites) self.drawSprites(surface_blit, npcList, surface) if player is not None: rect = Rect(player.rect) rect.x -= self.screenRect.x rect.y -= self.screenRect.y player.drawAt(rect, surface, self.fontRendererMedium) # pointList = [] # for i in range(len(player.path) - 1, 0, -1): # pointList.append([player.path[i].rect.x + self.tileSize.x / 2, player.path[i].rect.y + self.tileSize.y / 2]) # if len(pointList) > 1: # pygame.draw.lines(surface, (100,100,100), False, pointList, 5) # Draw debug info to screen rect.x = 0 rect.y = 13 # surface_blit(self.fontRendererMedium.render("Nmbr Of tasks: " + str(len(npcList[0].taskList.listOfTasks)), False, (0, 0, 0)), rect) self.lostsprites = [] def drawSprites(self, surface_blit, AI, screen): if AI is not None: for npc in AI: rect = Rect(npc.rect.x - self.screenRect.x, npc.rect.y - self.screenRect.y, 0, 0) surface_blit(npc.image, rect) surface_blit(self.fontRendererMedium.render("C:" + str(npc.converted), False, (0, 0, 0)), rect) # # rect.y += self.fontRendererMedium.size("P")[1] # # surface_blit(self.fontRendererMedium.render(str(npc.needs.thirst), False, (0, 0, 0)), rect) # rect.y += self.fontRendererSmall.size("P")[1] # surface_blit(self.fontRendererSmall.render("S:" + str(npc.needs.sleep), False, (0, 0, 0)), rect) # # rect.y += self.fontRendererMedium.size("P")[1] # # surface_blit(self.fontRendererMedium.render(str(npc.needs.boredom), False, (0, 0, 0)), rect) rect.y += self.fontRendererSmall.size("P")[1] surface_blit(self.fontRendererBig.render(str(npc.currentState), False, (0, 0, 0)), rect) pointList = [] if npc.target is not None: pointList.append(npc.target+self.tileSize/2) for i in range(len(npc.path)-1, 0, -1): pointList.append([npc.path[i].rect.x+self.tileSize.x/2, npc.path[i].rect.y+self.tileSize.y/2]) # if npc.roamNode is not None: # pygame.draw.rect(screen, (0, 0, 0), Rect(npc.roamNode.pos.x+10,npc.roamNode.pos.y+10, 30, 30), 5) # pointList.append(npc.roamNode.pos+self.tileSize/2) if len(pointList) > 1: pygame.draw.lines(screen, (0,0,0), False, pointList, 5) # if npc.roamNode is not None: # pygame.draw.rect(screen, (0, 0, 0), Rect(npc.roamNode.pos.x+10,npc.roamNode.pos.y+10, 30, 30), 5) def drawMap(self, surface_blit, tiles): for spr in tiles: rect = Rect(spr.rect.x - self.screenRect.x, spr.rect.y - self.screenRect.y, 0, 0) self.spritedict[spr] = surface_blit(spr.image, rect) surface_blit(self.fontRendererSmall.render(str(spr.f), False, (0, 0, 0)), rect) for action in spr.userActions: rect.move_ip(0, 10) surface_blit(self.fontRendererSmall.render(str(action), False, (0, 0, 0)), rect) def drawDebug(self, surface, debugInfo): rect = Rect(0, 0, 10, 10) for arg in debugInfo: rect.y += self.fontRendererMedium.size("P")[1] surface.blit(self.fontRendererMedium.render(str(arg), False, (0, 0, 0)), rect)
class Player: def __init__(self, x, y, speed=(0, 0), fps=DEFAULT_FPS): self.fps = fps self.bounds = Rect(x, y, 50, 50) self.bounds.center = (x, y) self.speed = speed # (dx, dy) self._last_speed = speed self.direction = TO_THE_RIGHT self.image = load_image('hero01.jpeg', -1) self.frames = { TO_THE_RIGHT: load_frames('sprite-sheet-walking-girl.png', 6, 5), TO_THE_LEFT: load_frames('sprite-sheet-walking-girl.png', 6, 5, reverse=True) } self.animation_speed = 8 self.animation_tick = 0 self.animation_current_frame = 0 self.image_dx = (self.bounds.w - self.frames[TO_THE_LEFT][0].get_width()) // 2 self.image_dy = (self.bounds.h // 2 - self.frames[TO_THE_LEFT][0].get_height()) @property def x(self): return self.bounds.centerx @property def y(self): return self.bounds.centery @property def is_run(self): if self.speed == (0, 0): return False else: return True def set_fps(self, new_fps): self.fps = new_fps def handle_events(self, keys): dx = 0 dy = 0 if keys[pygame.K_a]: dx = -PLAYER_SPEED / self.fps if keys[pygame.K_d]: dx = PLAYER_SPEED / self.fps if keys[pygame.K_w]: dy = -PLAYER_SPEED / self.fps if keys[pygame.K_s]: dy = PLAYER_SPEED / self.fps self._last_speed = self.speed self.speed = (dx, dy) if self.speed[0] > 0: self.direction = TO_THE_RIGHT elif self.speed[0] < 0: self.direction = TO_THE_LEFT def move(self, dx, dy): self.bounds = self.bounds.move(int(dx), int(dy)) def update(self): if self.speed != (0, 0): self.move(*self.speed) # определяем спрайт для анимации персонажа if self.animation_tick == 0: self.animation_current_frame = (self.animation_current_frame + 1) % 5 if self.is_run: self.image = self.frames[self.direction][ self.animation_current_frame] else: self.image = self.frames[self.direction][12] self.animation_tick = (self.animation_tick + 1) % (60 // self.animation_speed) def draw(self, surface, camera): surface.blit(self.image, (self.bounds.x + self.image_dx - camera.x, self.bounds.y + self.image_dy - camera.y))
class Widget(Sprite): """Use Widget class for better movement tracking Widget class inherits from Sprite class. Test cases for class Widget >>> from widget import * >>> import pygame >>> s = pygame.surface.Surface((30,30)) >>> w = Widget(s, (0,0,30,30), (0,0)) >>> w.rect <rect(0, 0, 30, 30)> >>> w.update() >>> w.rect <rect(0, 0, 30, 30)> >>> w.getMovement() [0, 0] >>> w.setX(1) >>> w.getX() 1 >>> w.setY(4) >>> w.getY() 4 >>> w.setMovement((3,5)) >>> w.getMovement() (3, 5) >>> w.getName() (0, 0) >>> w.setPosition((5,7)) >>> w.getPosition() (5, 7) """ def __init__(self, image, rect, name=''): """Instantiate a widget with a given surface, rectangle, and (x,y) movement pair. """ Sprite.__init__(self) self.movement = [0, 0] self.rect = Rect(rect) self.lastRect = self.rect self.image = image self.name = name self.frames = [] self.frameIndex = 0 self.frameRate = 1 self.timeDelay = WIDGETFRAMES self.lastUpdate = 0 self.world = None self.undone = False self.id = self.rect.top + self.rect.left +\ self.rect.width + self.rect.height def attachToWorld(self, world): self.world = world self.id = self.world.curWidgetID self.world.curWidgetID += 1 def startAnimation(self, frames, startIndex, frameRate): self.frames = frames self.frameIndex = startIndex self.frameRate = frameRate self.image = self.frames[startIndex] self.lastUpdate = self.timeDelay def __str__(self): return str(self.rect.left) + str(self.rect.top) + str(self.id) def setMovement(self, vector): """Set movement with a pair""" if(self.movement != [0,0] and vector == [0,0]): self.world.dirtyGroup.add(self) self.movement = vector def getMovement(self): """Return movement as a pair""" return self.movement def setStop(self): """Set movement to 0""" self.setMovement([0,0]) def setY(self, y): """Set y-component of movement""" self.movement[1] = y def setX(self, x): """Set x-component of movement""" self.movement[0] = x def getX(self): """Get x-component of movement""" return self.movement[0] def getY(self): """Set y-component of movement""" return self.movement[1] def setPosition(self, pair): """Set x and y coords of Widget""" self.rect.topleft = pair def getPosition(self): """Get x and y coords of Widget""" return self.rect.topleft def update(self): """Move sprite according to its movement vector""" # Widget needs to be animated if (len(self.frames) > 0): if self.lastUpdate <= 0: self.frameIndex = (self.frameIndex+1)%(len(self.frames)) self.image = self.frames[self.frameIndex] self.lastUpdate = self.timeDelay self.world.dirtyGroup.add(self) else: self.lastUpdate -= 1 elif(self.getMovement != [0,0]): self.lastRect = Rect(self.rect) self.rect.move_ip(self.movement) self.world.dirtyGroup.add(self) def undoUpdate(self): """Widget returns to state prior to last update()""" self.rect = self.lastRect def getShadow(self): shadow = Sprite() shadow.rect = self.lastRect.move(0,0) return shadow def getName(self): """Get name of Widget""" return self.name
class Camera: """ Class for keeping track of the camera position. In early stage of development """ def __init__(self, screen_res, level_rect, x_speed=15, y_speed=15, left_threshold=10, right_threshold=75, up_threshold=10, down_threshold=75): """ :param screen_res: A tuple of int. (w, h) :param level_rect: A rectangle that covers all of the level :param x_speed: The horizontal speed of the camera :param y_speed: The vertical speed of the camera :param left_threshold: The percentage of screen to reach in order for the camera to scroll left :param right_threshold: The percentage of screen to reach in order for the camera to scroll right :param up_threshold: The percentage of screen to reach in order for the camera to scroll up :param down_threshold: The percentage of screen to reach in order for the camera to scroll down """ self.level_rect = level_rect self.horizontal_speed = x_speed self.vertical_speed = y_speed self.screen_res = screen_res self.rect = Rect((0, 0), screen_res) self.x_bound = self.level_rect.width - self.rect.width self.y_bound = self.level_rect.height - self.rect.height self.right_threshold = self.rect.width * right_threshold / 100 self.left_threshold = self.rect.width * left_threshold / 100 self.up_threshold = self.rect.height * up_threshold / 100 self.down_threshold = self.rect.height * down_threshold / 100 def pan_left(self, h_speed): if self.rect.x == 0: return if self.rect.move(-h_speed, 0).x < 0: self.rect.move_ip(-self.rect.x, 0) else: self.rect.move_ip(-h_speed, 0) def pan_right(self, h_speed): if self.rect.x == self.x_bound: return if self.rect.x + h_speed + self.rect.width > self.level_rect.width: self.rect.move_ip((self.level_rect.width - (self.rect.x + self.rect.width)), 0) else: self.rect.move_ip(h_speed, 0) def pan_down(self, v_speed): if self.rect.y == self.y_bound: return if self.rect.y + v_speed + self.rect.height > self.level_rect.height: self.rect.move_ip(0, (self.level_rect.height - self.rect.y - self.rect.height)) else: self.rect.move_ip(0, v_speed) def pan_up(self, v_speed): if self.rect.y == 0: return if self.rect.move(0, -v_speed).y < 0: self.rect.move_ip(0, -self.rect.y) else: self.rect.move_ip(0, -v_speed) def snap_to(self, player_rect): propose = self.rect.move(0, 0) propose.x = min(max(0, player_rect.x - self.screen_res[0] / 2), self.x_bound) propose.y = min(max(0, player_rect.y - self.screen_res[1] / 2), self.y_bound) self.rect = propose def update(self, player_rect, custom_speed=None): h_speed, v_speed = self.horizontal_speed, self.vertical_speed if custom_speed: h_speed, v_speed = custom_speed if player_rect.x - self.rect.x > self.right_threshold: self.pan_right(h_speed) elif player_rect.x - self.rect.x < self.left_threshold: self.pan_left(h_speed) if player_rect.y - self.rect.y > self.down_threshold: self.pan_down(v_speed) elif player_rect.y - self.rect.y < self.up_threshold: self.pan_up(v_speed)
class Ball(pygame.sprite.Sprite): def __init__(self, screenSize, loc, speed=200): super().__init__() self.image = pygame.image.load(r'./Image/ball.png').convert_alpha() self.image = transform.scale(self.image, (20, 20)) self.rect = Rect(loc[0], loc[1], self.image.get_width(), self.image.get_height()) vx = random.randint(10, 100) vy = random.randint(10, 100) # 方向向量 self.direction = Vector((vx, vy)).normalize() self.xlim = screenSize[0] self.ylim = screenSize[1] self.speed = speed def update(self, dt): if self.rect.bottom >= self.ylim: return False if self.rect.top <= 0: # 防止球已经超过边界,造成反复reverse的情况 beyond = -self.rect.top if beyond > 0: self.move('down', beyond) self.reverseY() if self.rect.left <= 0: beyond = -self.rect.left if beyond > 0: self.move('right', beyond) self.reverseX() if self.rect.right >= self.xlim: beyond = self.rect.right - self.xlim if beyond > 0: self.move('left', beyond) self.reverseX() shift = self.direction * self.speed * dt self.rect = self.rect.move(shift.x, shift.y) return True def reverseX(self): self.direction.x = -self.direction.x def move(self, direction='up', by=0): """往某方向移动多少距离""" if direction == 'up': self.rect = self.rect.move(0, -by) elif direction == 'down': self.rect = self.rect.move(0, by) elif direction == 'left': self.rect = self.rect.move(-by, 0) elif direction == 'right': self.rect = self.rect.move(by, 0) def reverseY(self): self.direction.y = -self.direction.y def baffleCollisionDetect(self, baffle): point = pygame.sprite.collide_mask(self, baffle) if point: beyond = self.rect.bottom - baffle.rect.top if beyond > 0: self.move('up', beyond) self.reverseY() # print(point) def obstacleCollisionDetect(self, group): res = pygame.sprite.spritecollide(self, group, True) if res != []: print('Collision with', res) self.reverseY()
def advance_frame(input_get=pygame.event.get): global PLATFORMS, HATS, SPIKES, SPRINGS, ENEMIES, _ENEMIES, FLAGS, CHEESE, BACK, SPAWN global X, Y, x_vel, y_vel, DOOR, HAT, STATE, CROUCH, mov, hub, DIR global counter, dframe, IGT, endcard plats = [] allplats = [] for pos, dim, idx in PLATFORMS: allplats.append(Rect(pos, (dim[0]*32, dim[1]*32))) if isnear(pos, dim): plats.append(Rect(pos, (dim[0]*32, dim[1]*32))) # update counters/clock counter += 1 dframe = (dframe + 1) % 12 IGT += CLOCK.tick(30) # draw update screen adjust_scroller() SCREEN.blit(get_screen(), (0, 0)) SCREEN.blit(get_HUD(), (0, 0)) pygame.display.update() # evaluate input jmp = 0 door = 0 for e in input_get(): if e.type == QUIT or e.type == KEYDOWN and e.key == K_ESCAPE: quit() if e.type == KEYDOWN: if e.key == K_LEFT: mov = max(mov - 1, -1) if e.key == K_RIGHT: mov = min(mov + 1, 1) if e.key == K_DOWN: CROUCH = min(CROUCH+1, 1) if e.key == K_SPACE: jmp += 1 if e.key == K_UP: door = True if e.type == KEYUP: if e.key == K_LEFT: mov = min(mov+1, 1) if e.key == K_RIGHT: mov = max(mov-1, -1) if e.key == K_DOWN: CROUCH = max(CROUCH-1, 0) # change state, update movement JUMP = _JUMP * 2 if HAT == "baseball" else _JUMP if STATE == "dmg": if counter == 1: sounds["death"].stop() sounds["death"].play() if counter < 10: return STATE = "stand" X, Y = SPAWN x_vel, y_vel = 0, 0 HAT = None ENEMIES = deepcopy(_ENEMIES) elif jmp and (STATE in ["stand", "run0", "run1", "slide", "wall"] or HAT == "propeller"): sounds["jump"].stop() sounds["jump"].play() if HAT == "propeller": y_vel = JUMP if STATE == "wall": y_vel = JUMP DIR *= -1 x_vel = max(SPEED * DIR, x_vel) if DIR > 0 else min(SPEED * DIR, x_vel) else: STATE = "squat" counter = 0 elif STATE == "squat": if counter >= 3: y_vel = JUMP elif CROUCH: STATE = "crouch" elif mov and STATE not in ['crouch']: if DIR == mov: x_vel = max(SPEED * DIR, x_vel) if DIR > 0 else min(SPEED * DIR, x_vel) elif not x_vel or abs(x_vel) <= 3: DIR = mov if (x_vel > 0 and mov < 0) or (x_vel < 0 and mov > 0): STATE == "slide" elif x_vel == 0: STATE = "stand" else: STATE = "slide" if mov and not STATE in ["run0", "run1", "crouch", "squat"]: if ((x_vel > 0 and mov < 0) or (x_vel < 0 and mov > 0)): STATE = "slide" else: STATE = "run0" counter = 0 if STATE.startswith("run") and counter >= 5: STATE = "run" + str((int(STATE[-1]) + 1) % 2) counter = 0 if y_vel < 0: STATE = "jump0" if y_vel > 0: STATE = "jump1" # friction and gravity if x_vel and not STATE.startswith("jump"): x_vel += friction if x_vel < 0 else -1 y_vel += grav if STATE in ["jump1", "run0", "run1", "stand", "slide"] and HAT == "sombraro" and not CROUCH: y_vel = 0 # animate relevent actors for spring in SPRINGS: if spring[3]: spring[3] -= 1 # enemy logic remove = [] for i in range(len(ENEMIES)): pos, name, d, f , c = ENEMIES[i] if not isnear(pos): continue c += 1 if name == "bone": if c % 2: f = (f + 1) % 2 pos = (pos[0] + BONESPEED, pos[1]) if d == 0 else (pos[0] - BONESPEED, pos[1]) if c > 50: remove.append(i) if name == "skeleton": if abs(pos[0] - X) < 410: if f == 0: d = 0 if X > pos[0] else 1 if c >= 8 and f == 0: f = 1 elif c >= 30 and f == 1: sounds["throw"].stop() sounds["throw"].play() f = 2 ENEMIES.append( [pos, 'bone', d, 0, 0] ) elif c > 40: f, c = 0, 0 else: f, c = 0, 0 if name == "zombie": if c % 4 == 0 and abs(pos[0] - X) < 512: hitbox = Rect((pos[0]+8, pos[1]), (32, 64)) if d == 0: footbox = Rect((pos[0] + 32 + ZOMBIESPEED, pos[1] + 64), (10, 10)) else: footbox = Rect((pos[0] - ZOMBIESPEED, pos[1]+64), (10, 10)) if hitbox.collidelist(allplats) == -1: d = 0 if X > pos[0] else 1 f = (f + 1) % 2 pos = (pos[0] + ZOMBIESPEED, pos[1]) if d == 0 else (pos[0] - ZOMBIESPEED, pos[1]) if hitbox.collidelist(allplats) != -1 or footbox.collidelist(allplats) == -1: pos = (pos[0] - ZOMBIESPEED*2, pos[1]) if d == 0 else (pos[0] + ZOMBIESPEED*2, pos[1]) if name == "snake": hitbox = Rect(pos, (64, 16)) if c % 4 == 0: f = (f + 1) % 2 pos = (pos[0] + SNAKESPEED, pos[1]) if d == 0 else (pos[0] - SNAKESPEED, pos[1]) if hitbox.collidelist(allplats) != -1: d = (d + 1) % 2 pos = (pos[0] + SNAKESPEED*2, pos[1]) if d == 0 else (pos[0] - SNAKESPEED*2, pos[1]) if name == "ghost": d = 0 if X > pos[0] else 1 if (d == 1 and DIR == 1) or (d == 0 and DIR == -1): f = 1 else: f = 0 if abs(pos[0] - X) + abs(pos[1] - Y) < 800 and f == 0: if abs(pos[0] - X) > 30: x = -1 if X < pos[0] else 1 else: x = 0 if abs(pos[1] - Y) > 30: y = -1 if Y < pos[1] else 1 else: y = 0 pos = (pos[0]+x, pos[1]+y) ENEMIES[i] = [pos, name, d, f, c] for i in remove[::-1]: ENEMIES.pop(i) # hit detection - platforms hitbox = Rect((X, Y), (32, 64)) if STATE != "crouch" else Rect((X, Y+32), (32, 32)) checklist = plats if hitbox.collidelist(checklist) != -1: STATE = "dmg" return if x_vel: while hitbox.move(x_vel, 0).collidelist(checklist) != -1: if STATE.startswith("jump"): STATE = "wall" x_vel += 1 if x_vel < 0 else -1 if y_vel: while hitbox.move(0, y_vel).collidelist(checklist) != -1: y_vel += 1 if y_vel < 0 else -1 if x_vel and y_vel: while hitbox.move(x_vel, y_vel).collidelist(checklist) != -1: y_vel += 1 if y_vel < 0 else -1 x_vel += 1 if x_vel < 0 else -1 # hats checklist = [Rect(pos, (46, 46)) for pos, hat in HATS] i = hitbox.collidelist(checklist) if i != -1: HAT = HATS[i][1] # flags checklist = [Rect(pos, (46, 64)) for pos in FLAGS] i = hitbox.collidelist(checklist) if i != -1: SPAWN = FLAGS[i] # spikes and enemies checklist = [] for pos, d in SPIKES: if not isnear(pos): continue if d == 0: checklist += [Rect((pos[0]+8, pos[1]), (16, 16)), Rect((pos[0], pos[1]+16), (32, 16))] elif d == 1: checklist += [Rect((pos[0], pos[1]+8), (16, 16)), Rect((pos[0]+16, pos[1]), (16, 32))] elif d == 2: checklist += [Rect((pos[0], pos[1]), (32, 16)), Rect((pos[0]+8, pos[1]+16), (16, 16))] elif d == 3: checklist += [Rect((pos[0], pos[1]), (16, 32)), Rect((pos[0]+16, pos[1]+8), (32, 16))] for pos, name, d, f, c in ENEMIES: if not isnear(pos): continue if name in ["bone", "ghost"]: checklist.append(Rect(pos, (32, 32))) elif name == "snake": checklist.append(Rect(pos, (64, 16))) else: checklist.append(Rect(pos, (32, 64))) i = hitbox.collidelist(checklist) if i != -1: STATE = "dmg" counter = 0 # springs checklist = [Rect(pos, (48, 48)) for pos, d, s, f in SPRINGS] i = hitbox.collidelist(checklist) if i != -1: if SPRINGS[i][3] != 10: sounds["spring"].stop() sounds["spring"].play() SPRINGS[i][3] = 10 if SPRINGS[i][1] == 0: y_vel = 0 - SPRINGS[i][2] elif SPRINGS[i][1] == 2: y_vel = SPRINGS[i][2] elif SPRINGS[i][1] == 3: DIR = 1 x_vel = SPRINGS[i][2] elif SPRINGS[i][1] == 1: DIR = -1 x_vel = -1 * SPRINGS[i][2] # cheese checklist = [] for pos, idx in CHEESE: if idx == 3: checklist.append(Rect(pos, (64, 64))) else: checklist.append(Rect(pos, (32, 32))) i = hitbox.collidelist(checklist) if i != -1: sounds["get"].stop() sounds["get"].play() if not CHEESE[i][1] == 3: INV.append(CHEESE.pop(i)) else: n = H end = True while end: CLOCK.tick(30) if n >= 0: n -= 10 SCREEN.blit(get_screen(), (0, 0)) SCREEN.blit(endcard, (0, n)) pygame.draw.rect(SCREEN, (200, 200, 200), Rect((380, n + 530), (256, 104))) SCREEN.blit(HEL64.render(str(IGT // 60000) +":"+ ("0" + str(IGT // 1000 % 60))[-2:], 0, (0, 0, 0)), (400, n + 550)) pygame.display.update() for e in pygame.event.get(): if e.type == QUIT or e.type == KEYDOWN and e.key == K_ESCAPE: quit() if e.type == KEYDOWN: if e.key == K_LEFT: mov += -1 if e.key == K_RIGHT: mov += 1 if e.key == K_DOWN: CROUCH += 1 if e.key == K_SPACE and n <= 0: end = False load_level(hublvl) hub=True if e.type == KEYUP: if e.key == K_LEFT: mov -= -1 if e.key == K_RIGHT: mov -= 1 if e.key == K_DOWN: CROUCH -= 1 # door if hub: doorlist = [Rect((256+(i*256), 64), (64, 64)) for i in range(len(LEVELS))] di = hitbox.collidelist(doorlist) # enter door if door and (hitbox.colliderect(Rect(DOOR, (64, 64))) or (hub and di != -1 and len(INV)>=needs[di] )): sounds["door"].stop() sounds["door"].play() n = 2 if not hub: X, Y = DOOR[0] + 16, DOOR[1] adjust_scroller() STATE = "jump1" while n < 980: surf = rotate(get_screen(), n) SCREEN.blit(surf, ((W-surf.get_width())//2, (H - surf.get_height())//2)) n += n // 2 CLOCK.tick(30) pygame.display.update() for e in pygame.event.get(): if e.type == QUIT or e.type == KEYDOWN and e.key == K_ESCAPE: quit() if e.type == KEYDOWN: if e.key == K_LEFT: mov += -1 if e.key == K_RIGHT: mov += 1 if e.key == K_DOWN: CROUCH += 1 if e.type == KEYUP: if e.key == K_LEFT: mov -= -1 if e.key == K_RIGHT: mov -= 1 if e.key == K_DOWN: CROUCH -= 1 if hub and di != -1: load_level(LEVELS[di]) hub = False else: load_level(hublvl) hub = True # apply final calculated movement X += x_vel Y += y_vel
class GameObject: """ GameObject class based on pygame.Rect. This is a simple square object with bounds properties, which also has speed and can move. :param x: left coordinate :param y: top coordinate :param w: object width :param h: object height :param speed: object speed """ def __init__(self, x: int, y: int, w: int, h: int, speed: tuple = (0, 0)): """Init GameObject with certain features.""" self.bounds = Rect(x, y, w, h) self.speed = speed @property def left(self): """Return left bound.""" return self.bounds.left @property def right(self): """Return right bound.""" return self.bounds.right @property def top(self): """Return top bound.""" return self.bounds.top @property def bottom(self): """Return bottom bound.""" return self.bounds.bottom @property def width(self): """Return width.""" return self.bounds.width @property def height(self): """Return height.""" return self.bounds.height @property def center(self): """Return rectangle center.""" return self.bounds.center @property def centerx(self): """Return rectangle horisontal center.""" return self.bounds.centerx @property def centery(self): """Return rectangle vertical center.""" return self.bounds.centery def draw(self, surface): """ Draw object. Needs to be updated in child classes """ pass def move(self, dx: int, dy: int): """Move object. :param dx: horisontal step :param dy: vertical step """ self.bounds = self.bounds.move(dx, dy) def update(self): """Update object position.""" if self.speed == [0, 0]: return self.move(*self.speed)
class GameObject: def __init__(self, x, y, w, h, visible=True, speed=(0, 0)): self._bounds = Rect(x, y, w, h) self._visible = visible self._speed = speed @property def left(self): return self._bounds.left @property def right(self): return self._bounds.right @property def top(self): return self._bounds.top @property def bottom(self): return self._bounds.bottom @property def width(self): return self._bounds.width @property def height(self): return self._bounds.height @property def center(self): return self._bounds.center @property def centerx(self): return self._bounds.centerx @property def centery(self): return self._bounds.centery @property def visible(self): return self._visible def set_visible(self): self._visible = True def set_invisible(self): self._visible = False def in_bounds(self, pos): return self._bounds.collidepoint(pos) def draw(self, surface, dx=0, dy=0): pass def move(self, dx, dy): self._bounds = self._bounds.move(dx, dy) def update(self): pass
else: y = 0 pos = (pos[0]+x, pos[1]+y) ENEMIES[i] = [pos, name, d, f, c] for i in remove[::-1]: ENEMIES.pop(i) # hit detection - platforms hitbox = Rect((X, Y), (32, 64)) if STATE != "crouch" else Rect((X, Y+32), (32, 32)) checklist = plats if hitbox.collidelist(checklist) != -1: STATE = "dmg" continue if x_vel: while hitbox.move(x_vel, 0).collidelist(checklist) != -1: if STATE.startswith("jump"): STATE = "wall" x_vel += 1 if x_vel < 0 else -1 if y_vel: while hitbox.move(0, y_vel).collidelist(checklist) != -1: y_vel += 1 if y_vel < 0 else -1 if x_vel and y_vel: while hitbox.move(x_vel, y_vel).collidelist(checklist) != -1: y_vel += 1 if y_vel < 0 else -1 x_vel += 1 if x_vel < 0 else -1 # hats checklist = [Rect(pos, (46, 46)) for pos, hat in HATS] i = hitbox.collidelist(checklist) if i != -1: HAT = HATS[i][1] # flags checklist = [Rect(pos, (46, 64)) for pos in FLAGS] i = hitbox.collidelist(checklist)