def run(): pygame.init() pygame.display.set_caption("Ballsurf") fps = 60 default_millis = 1000 / fps fps_clock = pygame.time.Clock() width, height = 1280, 720 screen: pygame.Surface = pygame.display.set_mode((width, height)) game = Game() context = Context() # Game loop. while True: screen.fill((0, 0, 0)) context.key_strokes = set() context.resize = False context.letter = '' # Get system events for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() elif event.type == pygame.VIDEORESIZE: width, height = event.dict['size'] pygame.display.set_mode((width, height)) context.resize = True CachedImage.clear_cache() if event.type == pygame.KEYDOWN: if event.key == 8: context.key_strokes.add(Key.BACKSPACE) else: context.letter = event.unicode # Key key strokes keys = pygame.key.get_pressed() if keys[K_SPACE]: context.key_strokes.add(Key.SPACE) if keys[K_RETURN]: context.key_strokes.add(Key.ENTER) if keys[K_DOWN]: context.key_strokes.add(Key.NEXT) if keys[K_UP]: context.key_strokes.add(Key.PREV) if keys[K_ESCAPE]: context.key_strokes.add(Key.ESCAPE) game.update(context) size = min(width, height) game.render(screen, size / Const.game_height) pygame.display.flip() context.time_factor = fps_clock.tick(fps) / default_millis
def __init__(self): self.__x = 0 self.meters = Const.offset_meters self.image = CachedImage("res/img/noice.png") self.border = self.image.get_width() * Const.pixel_size self.random_line = [-1, 1] self.score = score.load_score()
def __init__(self, x=None): if x is None: self.x = 2 * Const.game_height else: self.x = x self.image = CachedImage(self.filename()) self.diameter = self.image.get_width() * Const.pixel_size self.y = Const.game_height - self.diameter self.__vspeed = 0 self.bounced = False self.hit_bottom = False self.id = uuid.uuid4()
def __init__(self, **kwargs): super(Direntry, self).__init__(**kwargs) with self.canvas: Color(0, 0, 0, 1) self.r = Rectangle() self.ci = CachedImage(pos_hint={'x': 0, 'y': 0.25}, size_hint=(1, 0.75), fill=True) with self.ci.canvas.before: Color(0, 0, 0, 1) self.ci.r = Rectangle() self.add_widget(self.ci) self.l = Label(pos_hint={'x': 0.02, 'top': 0.24}, size_hint=(None, None)) self.l.bind(texture_size=lambda *a: setattr(self.l, 'size', a[1])) self.add_widget(self.l) self.bind(pos=self.update_pos, size=self.update_size, source=self.update_source, orientation=self.update_orientation, text=self.update_text) self.update_source(None, self.source) self.update_orientation(None, self.orientation) self.update_text(None, self.text)
def __init__(self, filename: str, top: float, speed: float, x: Optional[float] = None, mirror: bool = True): if x is None: x = 2 self.x = x * Const.game_height self.speed = speed self.image = CachedImage(filename, mirror and random.getrandbits(1)) self.height = self.image.get_height() * Const.pixel_size self.width = self.image.get_width() * Const.pixel_size self.y = top
class Background(Sprite, ABC): def __init__(self, filename: str, top: float, speed: float, x: Optional[float] = None, mirror: bool = True): if x is None: x = 2 self.x = x * Const.game_height self.speed = speed self.image = CachedImage(filename, mirror and random.getrandbits(1)) self.height = self.image.get_height() * Const.pixel_size self.width = self.image.get_width() * Const.pixel_size self.y = top def update(self, context: Context, sprites: Sprites): self.x -= context.x_delta * self.speed def render(self, surface: pygame.Surface, size_factor: float): h = int(self.height * size_factor) w = int(self.width * size_factor) img = self.image.scale(w, h) l = int(self.x * size_factor) rect = pygame.Rect(0 if l >= 0 else abs(l), 0, w + min(0, l), h) surface.blit(img, (max(0, l), self.y * size_factor), rect) @property def box(self) -> GameRect: return GameRect(self.x, self.y, self.width, self.height) def can_delete(self) -> bool: return self.x <= -self.width
def got_dir(self, req, res, dt=0): if req: if res == rescache.get(req.url): return else: self.clear_widgets() rescache.set(req.url, res) sdir, direntries = get_direntries(res) files = [de for de in direntries if de[2] == FILE] jurl = self.server_url + urljoin('jpeg', quote(sdir.encode('utf-8')), '') url = self.server_url + urljoin(quote(sdir.encode('utf-8')), '') index = i = 0 for (fn, orig_orientation, file_type) in files: fn = quote(fn.encode('utf-8')) if fn[-4:].lower() in (".jpg", "jpeg"): file_url = url + fn else: file_url = jurl + fn + '.jpg' orientation = orig_orientation if platform == 'android': orientation = {1: 8, 3: 6, 6: 6, 8: 8}[orig_orientation] image = CachedImage(source=file_url, orientation=orientation, load=False, allow_scale=True) image.orig_orientation = orig_orientation image.bind(image_scale=self.on_image_scale) self.add_widget(image) if fn == self.filename: index = i i +=1 self.index = index
def __init__(self): self.__vpos = 0.5 self.__vspeed = -0.005 self.__bounced = False self.__last_ball = None self.images = { t: CachedImage(f"res/img/man_{t.value}.png") for t in ImageType } self.__target_image = ImageType.LARGE self.__last_image_change = 0 self.set_image(ImageType.LARGE)
class Tartan(Sprite): def __init__(self): self.__x = 0 self.meters = Const.offset_meters self.image = CachedImage("res/img/noice.png") self.border = self.image.get_width() * Const.pixel_size self.random_line = [-1, 1] self.score = score.load_score() def update(self, context: Context, sprites: Sprites): self.__x -= context.x_delta self.meters = context.meters while self.__x < -self.border: self.__x += self.border self.random_line[0] -= context.x_delta if self.random_line[0] < 0 and random.random() < 0.05: self.random_line[0] = 3 * Const.game_height self.random_line[1] = random.randint(1, 2) def render(self, surface: pygame.Surface, size_factor: float): area = Const.tartan_area(surface) surface.fill((156, 67, 47), area) size = int(self.image.get_width() * Const.pixel_size * size_factor) img = self.image.scale(size, size) for i in range(0, surface.get_width() // size + 2): surface.blit(img, (i * size + self.__x * size_factor, area.top)) t = int((Const.tartan_top + Const.pixel_size) * size_factor) h = int(Const.tartan_height * 0.38 * size_factor) lines = [ t, t + h, t + 2 * h ] for y in lines: for x in range(0, 3 * Const.game_height): surface.fill((255, 200, 200), pygame.Rect( int((x + self.__x) * size_factor), y, int(size_factor * 1), int(size_factor * Const.pixel_size) )) m = 50 next_meter_in = m - (self.meters - Const.player_position) % m next_meter_str = str(int(((self.meters - Const.player_position) // m + 1) * m)) surface.fill((255, 200, 200), pygame.Rect( int(next_meter_in * size_factor), lines[0], int(size_factor * Const.pixel_size), h )) font = pygame.font.Font("res/arcade.ttf", h) img = font.render(next_meter_str, True, (255, 200, 200)) surface.blit(img, (int((next_meter_in - 2 * Const.pixel_size) * size_factor) - img.get_width(), int(lines[0] + h / 2 - img.get_height() * 0.45))) surface.fill((255, 200, 200), pygame.Rect( int(self.random_line[0] * size_factor), lines[self.random_line[1]], int(size_factor * Const.pixel_size), h )) PADDING = Const.pixel_size * 2 for i, (name, s) in reversed(list(enumerate(self.score))): left = s - (self.meters - Const.player_position) if -Const.game_height < left < Const.game_height * 3: text = f"{i + 1} {name}".rstrip() img = font.render(text, True, (255, 255, 255)) pixel = int(Const.pixel_size * size_factor) surface.fill((133, 94, 66), pygame.Rect( int(left * size_factor) - pixel, int(Const.tartan_top * size_factor - h), int(size_factor * Const.pixel_size) + 2 * pixel, h )) l = int((left - PADDING) * size_factor - img.get_width() // 2) surface.fill((106, 75, 53), pygame.Rect( max(0, l - pixel), int(Const.tartan_top * size_factor - h * 1.5) - pixel, img.get_width() + PADDING * 2 * size_factor + min(0, l) + 2 * pixel, img.get_height() + 2 * pixel )) surface.fill((133, 94, 66), pygame.Rect( max(0, l), int(Const.tartan_top * size_factor - h * 1.5), img.get_width() + PADDING * 2 * size_factor + min(0, l), img.get_height() )) surface.blit(img, ( int(left * size_factor - img.get_width() // 2), int(Const.tartan_top * size_factor - h * 1.5) )) def box(self) -> GameRect: return GameRect(0, 0, 1, 1) def type(self) -> Type: return Type.TARTAN
class Ball(Sprite, ABC): def __init__(self, x=None): if x is None: self.x = 2 * Const.game_height else: self.x = x self.image = CachedImage(self.filename()) self.diameter = self.image.get_width() * Const.pixel_size self.y = Const.game_height - self.diameter self.__vspeed = 0 self.bounced = False self.hit_bottom = False self.id = uuid.uuid4() def update(self, context: Context, sprites: Sprites): self.x -= context.x_delta if self.bounced: self.move_by_gravity(context) def render(self, surface: pygame.Surface, size_factor: float): rect = self.box.to_pygame(size_factor, False) if rect.width <= 0 or rect.height <= 0: return img = self.image.scale(rect.width, rect.height) surface.blit(img, (rect.left, rect.top)) def bounce(self, velocity: float): self.bounced = True self.__vspeed = -velocity / 2 def move_by_gravity(self, context: Context): # time in s t = context.time_factor / Const.fps # gravity in m/(s**2) a = context.gravity self.y = 1 / 2 * a * (t ** 2) + \ self.__vspeed * t + \ self.y self.__vspeed = a * t + self.__vspeed if self.y + self.diameter >= Const.game_height + self.diameter * 0.3 and not self.hit_bottom: self.__vspeed = -self.__vspeed * self.bounciness() self.hit_bottom = True elif self.y + self.diameter < Const.game_height + self.diameter * 0.3 and self.hit_bottom: self.hit_bottom = False @property def box(self) -> GameRect: offset = max(0, (self.y + self.diameter) - Const.game_height) y_delta = 0 if offset > self.diameter * 0.3: y_delta = offset - self.diameter * 0.3 offset = self.diameter * 0.3 return GameRect(self.x, self.y - y_delta, self.diameter, self.diameter - offset) def type(self) -> Type: return Type.BALL def can_delete(self) -> bool: return self.x <= -1 @abstractmethod def filename(self) -> str: pass @abstractmethod def bounciness(self) -> float: """ :return: by how much the velocity of the player is multiplied when he hits the ball """ pass @abstractmethod def immediate_speed_increase(self) -> float: pass @abstractmethod def desired_speed_increase(self) -> float: pass