From: Skullheadx <94652084+Skullheadx@users.noreply.github.com> Date: Mon, 3 Jul 2023 22:55:44 +0000 (-0400) Subject: PERFORMANCE FIXED X-Git-Tag: game~7 X-Git-Url: http://git.skullheadx.com/nixos/static/git-logo.png?a=commitdiff_plain;h=52368b1ec72d6b221b355fb230255ed60be731c7;p=fruit-ninja.git PERFORMANCE FIXED --- diff --git a/bomb.py b/bomb.py index 911ffd8..9fae48f 100644 --- a/bomb.py +++ b/bomb.py @@ -9,53 +9,36 @@ class Bomb(Fruit): EXPLOSION_RADIUS = RADIUS * 10 POWER = 75 - BOMB_IMAGE = pygame.transform.scale(pygame.image.load("assets/bomb.png").convert_alpha(), - (RADIUS * 2, RADIUS * 2)).convert_alpha() + BOMB_IMAGE = pygame.image.load("assets/bomb.png").convert_alpha() + BOMB_TXT = Texture.from_surface(renderer, BOMB_IMAGE) EXPLOSIONS = [ [ - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch1/File1.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch1/File2.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch1/File3.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch1/File4.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch1/File5.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch1/File6.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha() + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch1/File1.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch1/File2.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch1/File3.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch1/File4.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch1/File5.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch1/File6.png")) ], [ - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch2/File1.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch2/File2.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch2/File3.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch2/File4.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch2/File5.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch2/File6.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha() + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch2/File1.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch2/File2.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch2/File3.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch2/File4.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch2/File5.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch2/File6.png")) ], [ - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch2/File1.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch2/File2.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch2/File3.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch2/File4.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch2/File5.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha(), - pygame.transform.scale(pygame.image.load(f"assets/explosion/Punch2/File6.png"), - (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)).convert_alpha() + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch2/File1.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch2/File2.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch2/File3.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch2/File4.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch2/File5.png")), + Texture.from_surface(renderer, pygame.image.load(f"assets/explosion/Punch2/File6.png")) ], ] + EXPLOSION_TIME = 500 explosion_sound_effects = [ @@ -66,27 +49,28 @@ class Bomb(Fruit): def __init__(self): super().__init__() self.radius = self.RADIUS + self.image = self.BOMB_IMAGE self.exploded = False + self.exploded_frame_timer = 0 - self.image = self.BOMB_IMAGE self.explosion_frame = 0 - self.explosion = random.choice(self.EXPLOSIONS) + + self.explosion_txt = random.choice(self.EXPLOSIONS) def update(self, delta): super().update(delta) if self.exploded: self.exploded_frame_timer += delta - if self.exploded_frame_timer >= self.EXPLOSION_TIME / len(self.explosion): + if self.exploded_frame_timer >= self.EXPLOSION_TIME / len(self.explosion_txt): self.exploded_frame_timer = 0 - self.explosion_frame = min(len(self.explosion) - 1, self.explosion_frame + 1) - self.image = self.explosion[self.explosion_frame] - if self.explosion_frame == len(self.explosion) - 1: + self.explosion_frame = min(len(self.explosion_txt) - 1, self.explosion_frame + 1) + if self.explosion_frame == len(self.explosion_txt) - 1: return True def explode(self, fruits, bombs, effects, depth=0): if self in bombs: if not self.exploded: - self.image = self.explosion[0] + self.image = self.explosion_txt[0] if depth == 0: pygame.mixer.Sound.play(random.choice(self.explosion_sound_effects)) self.exploded = True @@ -96,22 +80,20 @@ class Bomb(Fruit): for fruit in fruits: fruit.velocity += (fruit.position - self.position).normalize() * self.POWER for effect in effects: - if isinstance(effect, SplitEffect): - effect.velocity += (effect.position - self.position).normalize() * self.POWER + effect.velocity += (effect.position - self.position).normalize() * self.POWER for bomb in bombs: if not bomb.exploded: - bomb.explode(fruits, bombs, effects,depth+1) + bomb.explode(fruits, bombs, effects, depth + 1) - def draw(self, surf): + def draw(self): if self.exploded: - surf.blit(self.image, self.image.get_rect( - topleft=(self.position.x - self.EXPLOSION_RADIUS, self.position.y - self.EXPLOSION_RADIUS))) + self.explosion_txt[self.explosion_frame].draw(None, pygame.Rect(self.position.x - self.EXPLOSION_RADIUS, + self.position.y - self.EXPLOSION_RADIUS, + self.EXPLOSION_RADIUS * 2, + self.EXPLOSION_RADIUS * 2)) else: - # pygame.draw.circle(surf, BLACK, self.position, self.radius) if self.position.y - self.radius <= HEIGHT: - rotated_image = pygame.transform.rotate(self.image, self.angle) - new_rect = rotated_image.get_rect(center=self.image.get_rect( - topleft=(self.position.x - self.radius, self.position.y - self.radius)).center) - self.width, self.height = new_rect.size - surf.blit(rotated_image, new_rect.topleft) + self.BOMB_TXT.draw(None, pygame.Rect(self.position.x - self.radius, self.position.y - self.radius, + self.radius * 2, self.radius * 2), self.angle, + (self.radius, self.radius)) diff --git a/combo_counter.py b/combo_counter.py index 644d9d3..f697d50 100644 --- a/combo_counter.py +++ b/combo_counter.py @@ -8,8 +8,9 @@ class ComboCounter: self.position = pygame.Vector2(position) self.velocity = pygame.Vector2(0, -100) self.time = self.LIFE_TIME - self.combo = combo + self.combo = f"x{combo}" self.text_surface = font_large.render(self.combo, True, DARK_GRAY) + self.text_txt = Texture.from_surface(renderer, self.text_surface) def update(self, delta): self.position += self.velocity * delta / 1000 @@ -17,5 +18,6 @@ class ComboCounter: if self.time <= 0: return True - def draw(self, surf): - surf.blit(self.text_surface, self.position) + def draw(self): + self.text_txt.draw(None, self.position - pygame.Vector2(self.text_surface.get_width() / 2, + self.text_surface.get_height() / 2)) diff --git a/effect.py b/effect.py index ba608dd..4b6462e 100644 --- a/effect.py +++ b/effect.py @@ -1,5 +1,3 @@ -import pygame - from setup import * @@ -15,49 +13,48 @@ class BloodEffect: def __init__(self, position, radius, color=None): self.position = pygame.Vector2(position) + self.radius = radius * lerp(self.RADIUS_RANGE[0], self.RADIUS_RANGE[1], random.random()) - self.time = self.LIFE_TIME - self.frame_timer = 0 + + self.life_time = 0 self.current_frame = 0 - self.angle = 0 - self.frames = [ - pygame.transform.rotate(pygame.transform.scale(frame, (int(self.radius * 2), int(self.radius * 2))), - self.angle) for frame in random.choice(self.blood_frames)] + + self.surf_frames = random.choice(self.blood_frames) + if color is None: color = random.choice(EFFECT_COLORS) - else: - color = color - for frame in self.frames: + + for frame in self.surf_frames: px_array = pygame.PixelArray(frame) px_array.replace(pygame.Color(250, 3, 35), pygame.Color(color)) px_array.close() + self.frame_txt = [Texture.from_surface(renderer, frame) for frame in self.surf_frames] + def update(self, delta): - self.time -= delta - self.frame_timer += delta - if self.frame_timer >= self.LIFE_TIME / len(self.frames): - self.frame_timer = 0 + self.life_time += delta + + if self.life_time >= self.LIFE_TIME / len(self.surf_frames) * (self.current_frame + 1): self.current_frame += 1 - if self.time <= 0: + + if self.life_time >= self.LIFE_TIME: return True - def draw(self, surf): - if self.time > 0: - surf.blit(self.frames[self.current_frame], (self.position.x - self.radius, self.position.y - self.radius)) - # pygame.draw.circle(surf, BLACK, self.position, self.radius) + def draw(self): + if self.life_time < self.LIFE_TIME: + self.frame_txt[self.current_frame].draw(None, pygame.Rect(self.position.x - self.radius, + self.position.y - self.radius, + self.radius * 2, self.radius * 2)) class BloodSplatter: LIFE_TIME = 4000 FADE_TIME = 1000 - RADIUS_RANGE = [3, 3.75] + RADIUS_RANGE = [2, 2.75] + ANGLE_OFFSET = 35 blood_frames = [ - pygame.transform.rotate(pygame.image.load( - "assets/effects/splatter/bloodslash1.png"), - -35).convert_alpha(), - pygame.transform.rotate(pygame.image.load( - "assets/effects/splatter/bloodslash2.png"), - -35).convert_alpha() + pygame.image.load("assets/effects/splatter/bloodslash1.png").convert_alpha(), + pygame.image.load("assets/effects/splatter/bloodslash2.png").convert_alpha() ] LIGHT_COLOR1 = pygame.Color(110, 110, 110) @@ -65,7 +62,6 @@ class BloodSplatter: LIGHT_COLOR2 = pygame.Color(83, 83, 83) DARK_COLOR2 = pygame.Color(74, 74, 74) - color_frames = [dict(), dict()] for c in EFFECT_COLORS: for i, f in enumerate(blood_frames): @@ -76,46 +72,41 @@ class BloodSplatter: px_array.close() color_frames[i][c] = c_f - def __init__(self, position, radius, angle, color=None): self.position = pygame.Vector2(position) + self.radius = radius * lerp(self.RADIUS_RANGE[0], self.RADIUS_RANGE[1], random.random()) - self.time = self.LIFE_TIME - self.fade_time = self.FADE_TIME self.angle = angle + + self.life_time = self.LIFE_TIME + self.fade_time = self.FADE_TIME + img_index = random.randint(0, len(self.blood_frames) - 1) - # self.frame = pygame.transform.rotate( - # pygame.transform.scale(self.blood_frames[img_index], (int(self.radius * 2), int(self.radius * 2))), - # self.angle) self.alpha = 255 if color is None: color = random.choice(EFFECT_COLORS) - self.frame = pygame.transform.rotate( - pygame.transform.scale(self.color_frames[img_index][color], (int(self.radius * 2), int(self.radius * 2))), - self.angle) - - # px_array = pygame.PixelArray(self.frame) - # px_array.replace(self.LIGHT_COLOR1 if img_index == 0 else self.LIGHT_COLOR2, pygame.Color(color)) - # px_array.replace(self.DARK_COLOR1 if img_index == 0 else self.DARK_COLOR2, pygame.Color(darken(color, 0.875))) - # px_array.close() + self.frame = Texture.from_surface(renderer, self.color_frames[img_index][color]) def update(self, delta): - if self.time > 0: - self.time -= delta + if self.life_time > 0: + self.life_time -= delta else: self.fade_time -= delta - if self.fade_time <= 0 and self.time <= 0: + if self.fade_time <= 0 and self.life_time <= 0: return True - if self.time <= 0: - self.alpha = int(lerp(0, 255, self.fade_time / self.FADE_TIME)) - def draw(self, surf): + if self.life_time <= 0: + self.alpha = int(lerp(0, 255, self.fade_time / self.FADE_TIME)) + self.frame.alpha = self.alpha - self.frame.set_alpha(self.alpha) - surf.blit(self.frame, (self.position.x - self.radius, self.position.y - self.radius)) + def draw(self): + self.frame.draw(None, pygame.Rect(self.position.x - self.radius, + self.position.y - self.radius, + self.radius * 2, self.radius * 2), + self.angle + self.ANGLE_OFFSET, origin=(self.radius, self.radius)) class SplitEffect: @@ -128,36 +119,36 @@ class SplitEffect: random.random()) / 100 + pygame.Vector2(normal_velocity) self.acceleration = pygame.Vector2(0, self.gravity) - # self.angle = lerp(-45, 45, random.random()) self.angle = 0 self.direction = random.choice([-1, 1]) - # self.angle = 0 - # self.direction = 0 - - self.frame = frame - self.width, self.height = self.frame.get_size() + self.frame = Texture.from_surface(renderer, frame) + self.width, self.height = self.frame.width, self.frame.height def update(self, delta): self.velocity += self.acceleration * delta / 1000 self.position += self.velocity * delta / 1000 + self.angle += 360 * delta / 1000 / 10 * self.direction if self.position.y - self.height / 2 > HEIGHT: return True - def draw(self, surf): - rotated_image, position = rotate_center(self.frame, self.angle, self.position) - self.width, self.height = rotated_image.get_size() - surf.blit(rotated_image, position) + def draw(self): + self.frame.draw(None, pygame.Rect(self.position.x - self.width / 2, + self.position.y - self.height / 2, + self.width, self.height), + self.angle, origin=(self.width / 2, self.height / 2)) + self.width, self.height = self.frame.width, self.frame.height @staticmethod def find_normals(v): return pygame.Vector2(-v.y, v.x), pygame.Vector2(v.y, -v.x) @staticmethod - def should_split(image, angle, image_position, mouse_position, mouse_direction): - img, img_pos = rotate_center(image.copy(), angle, image_position) + def should_split(image, angle, image_position, mouse_position, mouse_direction, radius): + img, img_pos = rotate_center(pygame.transform.scale(image.copy(), (radius * 2, radius * 2)), angle, + image_position) img_pos += pygame.Vector2(img.get_size()) / 2 if mouse_direction.x == 0: mouse_direction.x += 0.0001 @@ -180,8 +171,11 @@ class SplitEffect: return False @staticmethod - def split_image(image, angle, image_position, mouse_position, mouse_direction): - img, img_pos = rotate_center(image.copy(), angle, image_position) + def split_image(image, angle, image_position, mouse_position, mouse_direction, radius): + img, img_pos = rotate_center(pygame.transform.scale(image.copy(), (radius * 2, radius * 2)), angle, + image_position) + + img_pos += pygame.Vector2(img.get_size()) / 2 if mouse_direction.x == 0: mouse_direction.x += 0.0001 @@ -202,21 +196,17 @@ class SplitEffect: t1 = (- mp.x) / mouse_direction.x p1 = mp + t1 * mouse_direction - t2 = (img.get_width() - mp.x) / mouse_direction.x - p2 = mp + t2 * mouse_direction - p3 = (p1 - rot_center).rotate(-a) + rot_center - p4 = (p2 - rot_center).rotate(-a) + rot_center half1 = img.subsurface(pygame.Rect(0, 0, img.get_width(), clamp(p3.y, 0, img.get_height()))) half2 = img.subsurface(pygame.Rect(0, clamp(p3.y, 0, img.get_height()), img.get_width(), clamp(img.get_height() - p3.y, 0, img.get_height()))) p5 = half1.get_rect().center - rot_center - pos1 = (p5).rotate(a) + img_pos + pos1 = p5.rotate(a) + img_pos p6 = half2.get_rect().center - rot_center + pygame.Vector2(0, clamp(p3.y, 0, img.get_height())) - pos2 = (p6).rotate(a) + img_pos + pos2 = p6.rotate(a) + img_pos r_half1 = pygame.transform.rotate(half1, -a) r_half2 = pygame.transform.rotate(half2, -a) @@ -225,54 +215,43 @@ class SplitEffect: class SlashEffect: - SLASH = [pygame.image.load(f"assets/effects/sword_slashes/White_Slash_Thin/File{i}.png").convert_alpha() for i in - range(1, 7)] - GROUP_SLASH = [pygame.image.load(f"assets/effects/sword_slashes/White_Group_Slashes/File{i}.png").convert_alpha() - for i in - range(1, 21)] + SLASH_SURFS = [pygame.image.load(f"assets/effects/sword_slashes/White_Slash_Thin/File{i}.png").convert_alpha() + for i in range(1, 7)] LIFETIME = 600 - def __init__(self, position, angle, combo=False): + def __init__(self, position, angle): self.position = pygame.Vector2(position) self.angle = angle - self.is_combo = combo - self.time = 0 - self.frame = 0 - - # if self.is_combo: - # self.slash_frames = [pygame.transform.rotate(frame, self.angle) for frame in self.GROUP_SLASH] - # else: - # self.slash_frames = [pygame.transform.rotate(frame, self.angle) for frame in self.SLASH] - if self.is_combo: - self.slash_frames = self.GROUP_SLASH - else: - self.slash_frames = [pygame.transform.rotate(frame, self.angle) for frame in self.SLASH] + self.current_frame = 0 + + self.slash_frames = [Texture.from_surface(renderer, frame) for frame in self.SLASH_SURFS] def update(self, delta): self.time += delta - if self.time >= self.LIFETIME / len(self.SLASH): + if self.time >= self.LIFETIME / len(self.SLASH_SURFS): self.time = 0 - self.frame += 1 - if self.frame >= len(self.SLASH): + self.current_frame += 1 + if self.current_frame >= len(self.SLASH_SURFS): return True - def draw(self, surf): - - frame = self.slash_frames[self.frame] - surf.blit(frame, frame.get_rect(center=self.position)) + def draw(self): + frame = self.slash_frames[self.current_frame] + frame.draw(None, self.position - pygame.Vector2(frame.width, frame.height) / 2, self.angle) class FadeInEffect: def __init__(self, fade_time=500): self.fade_time = fade_time - self.surf = pygame.Surface((WIDTH, HEIGHT)) + self.surf = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA) self.surf.fill(BLACK) self.time = self.fade_time self.alpha = 255 + self.txt = Texture.from_surface(renderer, self.surf) + def update(self, delta): self.time -= delta @@ -280,9 +259,9 @@ class FadeInEffect: return True self.alpha = int(lerp(0, 255, self.time / self.fade_time)) - def draw(self, surf): - self.surf.set_alpha(self.alpha) - surf.blit(self.surf, (0, 0)) + def draw(self): + self.txt.alpha = self.alpha + self.txt.draw(None, (0, 0)) class FadeOutEffect: @@ -291,17 +270,19 @@ class FadeOutEffect: self.fade_time = fade_time self.max_alpha = max_alpha - self.surf = pygame.Surface((WIDTH, HEIGHT)) + self.surf = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA) self.surf.fill(BLACK) self.time = 0 self.alpha = 0 + self.txt = Texture.from_surface(renderer, self.surf) + def update(self, delta): self.time += delta if self.time >= self.fade_time: return True self.alpha = int(lerp(0, self.max_alpha, self.time / self.fade_time)) - def draw(self, surf): - self.surf.set_alpha(self.alpha) - surf.blit(self.surf, (0, 0)) + def draw(self): + self.txt.alpha = self.alpha + self.txt.draw(None, (0, 0)) diff --git a/fruit.py b/fruit.py index 90128ec..aed62a3 100644 --- a/fruit.py +++ b/fruit.py @@ -2,7 +2,7 @@ from setup import * class Fruit: - RADIUS_RANGE = [60 * SCALE.x, 100 * SCALE.x] # [25, 50] + RADIUS_RANGE = [60 * SCALE.x, 100 * SCALE.x] HORIZONTAL_SPAWN_RANGE = [max(RADIUS_RANGE), WIDTH - max(RADIUS_RANGE)] VERTICAL_SPAWN_RANGE = [HEIGHT + max(RADIUS_RANGE), HEIGHT * 2 + max(RADIUS_RANGE)] @@ -11,10 +11,9 @@ class Fruit: HORIZONTAL_TARGET_RANGE = [WIDTH / 5.5, WIDTH * 4.5 / 5.5] GRAVITY = 275 * SCALE.y - HEADS = [ + FRUITS = [ pygame.image.load(f"assets/fruits/{file}").convert_alpha() for file in os.listdir('assets/fruits/') ] - OUTLINE_WIDTH = 3 def __init__(self): self.radius = lerp(self.RADIUS_RANGE[0], self.RADIUS_RANGE[1], random.random()) @@ -25,32 +24,30 @@ class Fruit: self.position = pygame.Vector2( lerp(self.HORIZONTAL_SPAWN_RANGE[0], self.HORIZONTAL_SPAWN_RANGE[1], random.random()), lerp(self.VERTICAL_SPAWN_RANGE[0], self.VERTICAL_SPAWN_RANGE[1], random.random())) - self.acceleration = pygame.Vector2(0, self.GRAVITY) - # self.previous_position = self.position + self.acceleration = pygame.Vector2(0, self.GRAVITY) dy = self.target.y - self.position.y dx = self.target.x - self.position.x t = (-2 / self.GRAVITY * dy) ** 0.5 self.velocity = pygame.Vector2(dx / t, -(-2 * self.GRAVITY * dy) ** 0.5) - self.image = pygame.transform.scale(random.choice(self.HEADS), (self.radius * 2, self.radius * 2)) + self.angle = lerp(0, 360, random.random()) self.direction = random.choice([-1, 1]) self.width, self.height = (self.radius * 2, self.radius * 2) + + self.image = random.choice(self.FRUITS) self.fruit_txt = Texture.from_surface(renderer, self.image) def update(self, delta): - # self.previous_position = self.position.copy() - self.velocity / 1000 * 30 self.velocity += self.acceleration * delta / 1000 self.position += self.velocity * delta / 1000 self.angle += 360 * delta / 1000 / 10 * self.direction - def get_rect(self): - return pygame.Rect(self.position - pygame.Vector2(self.radius / 2, self.radius / 2), - pygame.Vector2(self.radius, self.radius)) - def draw(self): if self.position.y - self.radius <= HEIGHT: - self.fruit_txt.draw(None, self.position - pygame.Vector2(self.radius,self.radius), angle=self.angle, origin=None) + self.fruit_txt.draw(None, pygame.Rect(self.position - pygame.Vector2(self.radius, self.radius), + (self.radius * 2, self.radius * 2)), + angle=-self.angle, origin=(self.radius, self.radius)) diff --git a/game.py b/game.py index eb3ecd6..9680b40 100644 --- a/game.py +++ b/game.py @@ -1,17 +1,15 @@ -import pygame - -from bomb import Bomb -from combo_counter import ComboCounter -from effect import BloodEffect, SplitEffect, SlashEffect, FadeInEffect, FadeOutEffect, BloodSplatter -from fruit import Fruit from player import Player +from fruit import Fruit +from effect import BloodEffect, SplitEffect, SlashEffect, FadeInEffect, FadeOutEffect, BloodSplatter +from combo_counter import ComboCounter +from bomb import Bomb +from rect import Rect from setup import * class Game: BOMB_CHANCE = 0 - EFFECT_COUNT_PER_FRUIT = 20 - EFFECT_COUNT_PER_BOMB = 0 + COMBO_TIME = 250 GAME_OVER_TIME = 2000 WAVE_COOLDOWN = 500 @@ -29,6 +27,8 @@ class Game: BACKGROUND.blit(dark_background_tile, (x * WIDTH / tile_cols, y * HEIGHT / tile_rows)) else: BACKGROUND.blit(background_tile, (x * WIDTH / tile_cols, y * HEIGHT / tile_rows)) + BACKGROUND = Texture.from_surface(renderer, BACKGROUND) + bass_sound_effect = pygame.mixer.Sound("assets/sounds/sub-bass-4-secondsssss-6241.wav") bass_sound_effect.set_volume(0.1) slash_sounds = [pygame.mixer.Sound(f"assets/sounds/Swishes/long-medium-swish-44324.wav"), @@ -36,25 +36,30 @@ class Game: pygame.mixer.Sound(f"assets/sounds/Swishes/swish-sound-94707.wav"), ] - HIGHSCORE_FILE = "highscore.txt" + HIGH_SCORE_FILE = "high_score.txt" def __init__(self): self.player = Player() + self.fruits = [Fruit()] self.bombs = [] + self.effects = [ - [], # Blood splatter - [], # Blood splash - [], # Split Effect - [], # slash effect - [FadeInEffect(fade_time=1000)] # Fade in/fade out effects + [], # Blood splatter + [], # Blood splash + [], # Split Effect + [], # slash effect + [FadeInEffect(fade_time=1000)] # Fade in/fade out effects ] + self.combo_counters = [] - self.wave = 10 + self.score = 0 + self.time_since_last_hit = 0 self.current_combo = 0 + self.wave = 100 self.cleared_wave = True self.wave_cooldown_timer = 0 @@ -62,88 +67,131 @@ class Game: self.game_over_time = 0 try: - with open(self.HIGHSCORE_FILE, "r") as f: - self.highscore = int(f.read()) + with open(self.HIGH_SCORE_FILE, "r") as f: + self.high_score = int(f.read()) except: - self.highscore = 0 + self.high_score = 0 self.music_started = False if pygame.mixer.music.get_busy(): pygame.mixer.music.fadeout(500) - background_music = pygame.mixer.music.load( - "assets/sounds/Of Far Different Nature - Friendly Trap (CC-BY).ogg") + pygame.mixer.music.load("assets/sounds/Of Far Different Nature - Friendly Trap (CC-BY).ogg") pygame.mixer.music.set_volume(0.25) pygame.mixer.music.play(-1) self.music_started = True + self.score_surf = font.render(f"SCORE {self.score}", True, WHITE) + self.score_txt = Texture.from_surface(renderer, self.score_surf) + + self.combo_surf = font.render(f"COMBO x{self.current_combo}", True, WHITE) + self.combo_txt = Texture.from_surface(renderer, self.combo_surf) + + self.high_score_surf = font.render(f"BEST {self.high_score}", True, WHITE) + self.high_score_txt = Texture.from_surface(renderer, self.high_score_surf) + + self.title_surf = font_large.render("GAME OVER", True, WHITE) + + self.subtitle_surf = font.render(f"HIGH SCORE {self.high_score}", True, WHITE) + + self.game_over_surf = pygame.Surface((max(self.title_surf.get_width(), self.subtitle_surf.get_width()), + self.title_surf.get_height() + self.subtitle_surf.get_height())) + self.game_over_surf.fill(GRAY) + self.game_over_surf.blit(self.title_surf, self.title_surf.get_rect( + center=(self.game_over_surf.get_width() / 2, self.title_surf.get_height() / 2))) + self.game_over_surf.blit(self.subtitle_surf, self.subtitle_surf.get_rect( + center=( + self.game_over_surf.get_width() / 2, + self.title_surf.get_height() + self.subtitle_surf.get_height() / 2))) + self.game_over_txt = Texture.from_surface(renderer, self.game_over_surf) + + self.r1 = Rect(self.game_over_surf.get_rect(center=(WIDTH / 2, HEIGHT / 2)).inflate(50, 50), GRAY, 10) + self.r2 = Rect(self.game_over_surf.get_rect(center=(WIDTH / 2, HEIGHT / 2)).inflate(50, 50), BLACK, 10, 5) + def update(self, delta): for event in pygame.event.get(): - if event.type == pygame.QUIT: + if event.type == pygame.QUIT or (event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE): + if self.score > self.high_score: + self.high_score = self.score + with open(self.HIGH_SCORE_FILE, "w") as f: + f.write(str(self.high_score)) return COMMAND_EXIT - if event.type == pygame.KEYUP: - if event.key == pygame.K_ESCAPE: - return COMMAND_EXIT - elif event.key == pygame.K_m: + elif event.type == pygame.KEYUP: + if event.key == pygame.K_m: if pygame.mixer.music.get_busy(): pygame.mixer.music.pause() else: pygame.mixer.music.unpause() if not self.music_started: - background_music = pygame.mixer.music.load( - "assets/sounds/Of Far Different Nature - Friendly Trap (CC-BY).ogg") + pygame.mixer.music.load("assets/sounds/Of Far Different Nature - Friendly Trap (CC-BY).ogg") pygame.mixer.music.set_volume(0.25) pygame.mixer.music.play(-1) self.music_started = True + if not self.game_over: self.player.update(delta) else: self.game_over_time += delta if self.game_over_time > self.GAME_OVER_TIME: return COMMAND_START - hits = [] + hits = [] for fruit in self.fruits: fruit.update(delta) + hit_status = self.player.hits(fruit) - if hit_status and SplitEffect.should_split(fruit.image, fruit.angle, fruit.position, self.player.previous_mouse_pos, self.player.mouse_direction): + if hit_status and SplitEffect.should_split(fruit.image, fruit.angle, fruit.position, + self.player.previous_mouse_pos, + self.player.mouse_direction, fruit.radius): hits.append((fruit, self.player.mouse_direction, self.player.previous_mouse_pos)) - fr = fruit.get_rect() - if ((not -fruit.width < fr.x < WIDTH + fruit.width) or fr.y - fr.height > HEIGHT) and fruit.velocity.y > 0: + if (((not -fruit.radius * 2 < fruit.position.x < WIDTH + fruit.radius * 2) or + fruit.position.y - fruit.radius * 2 > HEIGHT) and fruit.velocity.y > 0): self.fruits.remove(fruit) self.cleared_wave = False + self.time_since_last_hit += delta if self.time_since_last_hit < self.COMBO_TIME: self.score += self.current_combo + self.score_surf = font.render(f"SCORE {self.score}", True, WHITE) + self.score_txt = Texture.from_surface(renderer, self.score_surf) + self.high_score_surf = font.render(f"BEST {self.high_score}", True, WHITE) + self.high_score_txt = Texture.from_surface(renderer, self.high_score_surf) else: self.current_combo = 0 + self.combo_surf = font.render(f"COMBO x{self.current_combo}", True, WHITE) + self.combo_txt = Texture.from_surface(renderer, self.combo_surf) for hit, mouse_direction, mouse_position in hits: - color = random.choice(EFFECT_COLORS) - self.effects[0].append( - BloodSplatter(hit.position, hit.radius, determine_angle(hit.position, hit.position + mouse_direction),color)) - self.effects[1].append(BloodEffect(hit.position, hit.radius,lighten(color, 0.15))) + self.effects[0].append(BloodSplatter(hit.position, hit.radius, + determine_angle(hit.position, hit.position + mouse_direction), + color)) + self.effects[1].append(BloodEffect(hit.position, hit.radius, lighten(color, 0.15))) - half1, half2, pos1, pos2 = SplitEffect.split_image(hit.image, hit.angle, hit.position, mouse_position, mouse_direction) + half1, half2, pos1, pos2 = SplitEffect.split_image(hit.image, hit.angle, hit.position, mouse_position, + mouse_direction, hit.radius) - n1, n2 = SplitEffect.find_normals(mouse_direction.normalize()) - c = 100 - self.effects[2].append(SplitEffect(pos1, half1, hit.velocity, n1 * c)) - self.effects[2].append(SplitEffect(pos2, half2, hit.velocity, n2 * c)) + n1, n2 = SplitEffect.find_normals(mouse_direction.normalize() * 5) + self.effects[2].append(SplitEffect(pos1, half1, hit.velocity, n1)) + self.effects[2].append(SplitEffect(pos2, half2, hit.velocity, n2)) pygame.mixer.Sound.play(random.choice(self.slash_sounds)) self.score += 1 + self.score_surf = font.render(f"SCORE {self.score}", True, WHITE) + self.score_txt = Texture.from_surface(renderer, self.score_surf) + self.high_score_surf = font.render(f"BEST {self.high_score}", True, WHITE) + self.high_score_txt = Texture.from_surface(renderer, self.high_score_surf) + if self.time_since_last_hit < self.COMBO_TIME: self.current_combo += 1 + self.combo_surf = font.render(f"COMBO x{self.current_combo}", True, WHITE) + self.combo_txt = Texture.from_surface(renderer, self.combo_surf) if self.current_combo > 1: - self.combo_counters.append(ComboCounter(hit.position, f"x{self.current_combo + 1}")) - self.effects[3].append(SlashEffect(hit.position, hit.angle, combo=False)) - else: - self.effects[3].append(SlashEffect(hit.position, hit.angle)) + self.combo_counters.append(ComboCounter(hit.position, self.current_combo + 1)) + self.effects[3].append(SlashEffect(hit.position, hit.angle)) self.time_since_last_hit = 0 if hit in self.fruits: @@ -154,6 +202,7 @@ class Game: effect_status = effect.update(delta) if effect_status: layer.remove(effect) + for combo in self.combo_counters: combo_status = combo.update(delta) if combo_status: @@ -161,22 +210,15 @@ class Game: for bomb in self.bombs: bomb_status = bomb.update(delta) - if self.player.hits(bomb): bomb.explode(self.fruits, self.bombs, self.effects[2]) - self.game_over = True - self.player.sliced_points.clear() - pygame.mixer.Sound.play(self.bass_sound_effect) - self.effects[4].append(FadeOutEffect(fade_time=self.GAME_OVER_TIME, max_alpha=20)) - if self.score > self.highscore: - self.highscore = self.score - with open(self.HIGHSCORE_FILE, "w") as f: - f.write(str(self.highscore)) + self.set_game_over() + if bomb_status: self.bombs.remove(bomb) continue - br = bomb.get_rect() - if ((not -bomb.width < br.x < WIDTH + bomb.width) or br.y - br.height > HEIGHT) and bomb.velocity.y > 0: + if (((not -bomb.radius * 2 < bomb.position.x < WIDTH + bomb.radius * 2) or + bomb.position.y - bomb.radius * 2 > HEIGHT) and bomb.velocity.y > 0): self.bombs.remove(bomb) if len(self.fruits) == 0 and len(self.bombs) == 0 and not self.game_over: @@ -192,46 +234,47 @@ class Game: else: self.fruits.append(Fruit()) - def draw(self, surf): + def set_game_over(self): + self.game_over = True - screen.blit(self.BACKGROUND, (0, 0)) + self.player.sliced_points.clear() + pygame.mixer.Sound.play(self.bass_sound_effect) + self.effects[4].append(FadeOutEffect(fade_time=self.GAME_OVER_TIME, max_alpha=20)) - text_surf = font.render(f"SCORE {self.score}", True, WHITE) - surf.blit(text_surf, (7, 0)) - text_surf2 = font.render(f"COMBO x{max(1, self.current_combo)}", True, WHITE) - surf.blit(text_surf2, text_surf2.get_rect(center=(WIDTH / 2, text_surf.get_height() / 2))) - # text_surf2 = font.render(f"TIME SINCE LAST HIT {round(self.time_since_last_hit / 1000, 1)}", True, BLACK) - # surf.blit(text_surf2, (WIDTH - text_surf2.get_width(), text_surf.get_height())) + if self.score > self.high_score: + self.high_score = self.score + with open(self.HIGH_SCORE_FILE, "w") as f: + f.write(str(self.high_score)) + + self.subtitle_surf = font.render(f"HIGH SCORE {self.high_score}", True, WHITE) + + def draw(self): + self.BACKGROUND.draw(None, (0, 0)) + + self.score_txt.draw(None, (7, 0)) + self.combo_txt.draw(None, (WIDTH / 2 - self.combo_txt.width / 2, 0)) + self.high_score_txt.draw(None, (WIDTH - self.high_score_txt.width - 7, 0)) for effect in self.effects[0]: - effect.draw(surf) + effect.draw() for effect in self.effects[1]: - effect.draw(surf) + effect.draw() for bomb in self.bombs: - bomb.draw(surf) + bomb.draw() for effect in self.effects[2]: - effect.draw(surf) + effect.draw() for fruit in self.fruits: - fruit.draw(surf) + fruit.draw() for effect in self.effects[3]: - effect.draw(surf) + effect.draw() for combo in self.combo_counters: - combo.draw(surf) - self.player.draw(surf) + combo.draw() + self.player.draw() for effect in self.effects[4]: - effect.draw(surf) + effect.draw() + if self.game_over: - title = font_large.render("GAME OVER", True, WHITE) - subtitle = font.render(f"HIGHSCORE {self.highscore}", True, WHITE) - game_over_surf = pygame.Surface( - (max(title.get_width(), subtitle.get_width()), title.get_height() + subtitle.get_height())) - game_over_surf.fill(GRAY) - game_over_surf.blit(title, title.get_rect(center=(game_over_surf.get_width() / 2, title.get_height() / 2))) - game_over_surf.blit(subtitle, subtitle.get_rect( - center=(game_over_surf.get_width() / 2, title.get_height() + subtitle.get_height() / 2))) - - pygame.draw.rect(surf, GRAY, game_over_surf.get_rect(center=(WIDTH / 2, HEIGHT / 2)).inflate(50, 50), - border_radius=10) - pygame.draw.rect(surf, BLACK, game_over_surf.get_rect(center=(WIDTH / 2, HEIGHT / 2)).inflate(50, 50), 5, - border_radius=10) - surf.blit(game_over_surf, game_over_surf.get_rect(center=(WIDTH / 2, HEIGHT / 2))) \ No newline at end of file + self.r1.draw() + self.r2.draw() + self.game_over_txt.draw(None, ( + WIDTH / 2 - self.game_over_txt.width / 2, HEIGHT / 2 - self.game_over_txt.height / 2)) diff --git a/highscore.txt b/high_score.txt similarity index 100% rename from highscore.txt rename to high_score.txt diff --git a/main.py b/main.py index f1051cc..b3d22be 100644 --- a/main.py +++ b/main.py @@ -1,8 +1,7 @@ -# from game import Game +from game import Game from menu import Menu from setup import * - FPS = 60 clock = pygame.time.Clock() @@ -13,21 +12,20 @@ while is_running: delta = clock.tick(FPS) renderer.clear() - status = scene.update(delta) scene.draw() - fps_text = font.render(f"FPS {int(clock.get_fps())}", True, WHITE) - fps_text_txt = Texture.from_surface(renderer, fps_text) - fps_text_txt.draw(None, (WIDTH - fps_text.get_width() - 7, 0)) + fps_text = font_small.render(f"FPS: {clock.get_fps():.0f}", True, DARK_GRAY) + fps_txt = Texture.from_surface(renderer, fps_text) + fps_txt.draw(None, pygame.Vector2(10, 75)) renderer.present() if status == COMMAND_EXIT: is_running = False - # elif status == COMMAND_START: - # scene = Game() - # elif status == COMMAND_MENU: - # scene = Menu() + elif status == COMMAND_START: + scene = Game() + elif status == COMMAND_MENU: + scene = Menu() pygame.quit() diff --git a/menu.py b/menu.py index e7254e2..2dcf1bf 100644 --- a/menu.py +++ b/menu.py @@ -38,8 +38,9 @@ class Menu: self.fruit = Fruit() self.fruit.position = pygame.Vector2(WIDTH / 2, HEIGHT * 1.5 / 2.5) self.fruit.angle = 0 - self.fruit.image = pygame.transform.scale(pygame.image.load("assets/fruits/58.png"), - (self.fruit.radius * 2, self.fruit.radius * 2)) + self.fruit.image = pygame.image.load("assets/fruits/58.png") + self.fruit.fruit_txt = Texture.from_surface(renderer, self.fruit.image) + # Effects self.effects = [] @@ -56,7 +57,7 @@ class Menu: # Rect textures self.tutorial_surface_pos = ( - WIDTH / 2, HEIGHT * 2 / 3 + self.tutorial_surface.get_height() / 2 + self.fruit.get_rect().height + 30) + WIDTH / 2, HEIGHT * 2 / 3 + self.tutorial_surface.get_height() / 2 + self.fruit.radius + 30) self.r1 = Rect(self.tutorial_surface.get_rect(center=self.tutorial_surface_pos).inflate(25, 25), DARK_GRAY, 10) self.r2 = Rect(self.tutorial_surface.get_rect(center=self.tutorial_surface_pos).inflate(25, 25), BLACK, 10, 5) @@ -88,19 +89,20 @@ class Menu: hit_status = self.player.hits(self.fruit) # Check if player hits fruit # Check if fruit should split if hit_status and SplitEffect.should_split(self.fruit.image, self.fruit.angle, self.fruit.position, - self.player.previous_mouse_pos, self.player.mouse_direction): + self.player.previous_mouse_pos, self.player.mouse_direction, + self.fruit.radius): # Split fruit color = random.choice(EFFECT_COLORS) n1, n2 = SplitEffect.find_normals(self.player.mouse_direction.normalize() * 5) - self.effects.append(BloodEffect(self.fruit.position, self.fruit.radius, lighten(color, 0.15))) - half1, half2, pos1, pos2 = SplitEffect.split_image(self.fruit.image, self.fruit.angle, - self.fruit.position, self.player.previous_mouse_pos, - self.player.mouse_direction) self.effects.append(BloodSplatter(self.fruit.position, self.fruit.radius, determine_angle(self.fruit.position, self.fruit.position + self.player.mouse_direction), color)) + self.effects.append(BloodEffect(self.fruit.position, self.fruit.radius, lighten(color, 0.15))) + half1, half2, pos1, pos2 = SplitEffect.split_image(self.fruit.image, self.fruit.angle, + self.fruit.position, self.player.previous_mouse_pos, + self.player.mouse_direction, self.fruit.radius) self.effects.append(SplitEffect(pos1, half1, pygame.Vector2(0, 0), n1)) self.effects.append(SplitEffect(pos2, half2, pygame.Vector2(0, 0), n2)) diff --git a/player.py b/player.py index 7883670..fe66ea8 100644 --- a/player.py +++ b/player.py @@ -1,5 +1,3 @@ -import pygame - from setup import * @@ -20,7 +18,6 @@ class Player: self.display_image = self.IMAGE.copy() self.position = pygame.Vector2(0, 0) - def update(self, delta): pressed = pygame.mouse.get_pressed() if pressed[0]: @@ -46,7 +43,8 @@ class Player: self.lines.clear() if len(self.sliced_points) > 1: for i in range(len(self.sliced_points) - 1): - self.lines.append((pygame.Vector2(self.sliced_points[i][0]), pygame.Vector2(self.sliced_points[i + 1][0]))) + self.lines.append( + (pygame.Vector2(self.sliced_points[i][0]), pygame.Vector2(self.sliced_points[i + 1][0]))) for i, val in enumerate(self.sliced_points): pos, time = val @@ -80,4 +78,3 @@ class Player: for i in range(len(self.sliced_points) - 1): renderer.draw_line(self.sliced_points[i][0], self.sliced_points[i + 1][0]) renderer.draw_line(self.previous_mouse_pos, self.previous_mouse_pos - self.mouse_direction) - diff --git a/setup.py b/setup.py index 3ff1d53..7897215 100644 --- a/setup.py +++ b/setup.py @@ -112,8 +112,4 @@ def determine_angle(pos1, pos2): if pos1.x == pos2.x: pos2.x += 0.0001 a = math.degrees(math.atan((pos2.y - pos1.y) / (pos2.x - pos1.x))) - return -a - - - - + return a diff --git a/test.py b/test.py deleted file mode 100644 index 143e804..0000000 --- a/test.py +++ /dev/null @@ -1,33 +0,0 @@ -import pygame -from pygame._sdl2 import Window, Renderer, Texture - -class App: - - def __init__(self): - pygame.init() - - self.clock = pygame.time.Clock() - self.display = pygame.display.set_mode((1366, 768)) - self.window = Window.from_display_module() - self.renderer = Renderer(self.window) - self.renderer.draw_color = (255, 0, 0, 255) - - a_surface = pygame.image.load('image.png') - self.an_image = Texture.from_surface(self.renderer, a_surface) - - def run(self): - while True: - - for event in pygame.event.get(): - if event.type == pygame.QUIT: - pygame.quit() - raise SystemExit - - self.renderer.clear() - self.an_image.draw(None, (20, 20)) - self.renderer.present() - self.clock.tick(60) - - -app = App() -app.run() \ No newline at end of file