Why is the zoom feature of my game in pygame not working?

33 Views Asked by At

I've tried to add a zoom feature to my Top-Down Game in python, but I can't manage to figure out why the screen is tearing. (Problem in Game-Class)

import sys
import pygame as py  # Pygames
from pytmx.util_pygame import load_pygame as load_py

py.init()  # Pygame initialisieren
py.display.set_caption("Save The Babe")  # Titel des Fensters

#########################################################################
# Beginn der Klassendefinitionen
#########################################################################
TILESIZE = 16  # Grösse der Objekte (Pixel)

UI_font = "res/images/UI/StB.ttf"
UI_font_size = 18

#  Waffen einlesen
weapon_type = {
    "sword": {"cooldown": 100, "damage": 10, "graphic": "res/images/Player/Weapons/Sword/Sprite.png"},
    "axe": {"cooldown": 150, "damage": 18, "graphic": "res/images/Player/Weapons/Axe/Sprite.png"},
    "hammer": {"cooldown": 300, "damage": 30, "graphic": "res/images/Player/Weapons/Hammer/Sprite.png"},
    "staff": {"cooldown": 10000, "damage": 30, "graphic": "res/images/Player/Weapons/Staff/Sprite.png"}
}

#  Magie einlesen
magic_type = {
    "x_magic": {"damage": 100, "stam": 10, "graphic": "res/images/Player/Weapons/Sword/Sprite.png"},
    "y_magic": {"damage": 150, "stam": 18, "graphic": "res/images/Player/Weapons/Axe/Sprite.png"},
}


##########################################
# Die Klasse des Spiels, welche alle Objekte beinhalten sollte
##########################################
class Game:
    def __init__(self):
        self.display = None
        self.win_size = (1024, 768)  # Fenstergrösse
        self.screen = py.display.set_mode(self.win_size)  # Fenstergrösse setzen
        self.my_font = py.font.SysFont('Comic Sans MS', 36)

        # pytmx-setup
        # layers
        self.tmx_data = load_py("data/tsx/MapFinal.tmx")

        self.clock = py.time.Clock()
        self.game_clock = py.time.get_ticks()
        self.FPS = 60

        **self.zoom_level = 1.0
        self.zoom_amount = 0.1
        self.camera = py.Rect(0, 0, self.win_size[0], self.win_size[1])
**
        # Gruppen und Objekte je nach Eigenschaft
        self.visible_sprites = py.sprite.Group()  # Sichtbare Spritegruppe
        self.collisions_sprites = py.sprite.Group()  # Interagierbare Sprites

        self.camera = py.math.Vector2()  # Vektor für Kamera

        self.current_attack = None

        self.UI = UserInterface()  # UI aufrufen

    def create_map(self):
    

        for layer in self.tmx_data.layers:
            if hasattr(layer, "data"):
                for x, y, surf in layer.tiles():
                    pos = (x * 16, y * 16)
                    Object(pos=pos, groups=[self.visible_sprites], surf=surf)

        self.player = Player((1136, 854), [self.visible_sprites], self.create_weapon, self.destroy_weapon,
                             self.create_magic)
        Player.collisions_sprites = self.collisions_sprites

    #  Methode zur Waffenerstellung
    def create_weapon(self):
        self.current_attack = Weapon(self.player, [self.visible_sprites])

    #  Methode zur Waffenzerstörung
    def destroy_weapon(self):
        if self.current_attack:
            self.current_attack.kill()
        self.current_attack = None

    def create_magic(self, magic_kind, damage, stam):
        print(magic_kind, damage, stam)

   ** def handle_zoom(self, direction):
        if direction == "in":
            self.zoom_level += self.zoom_amount
        elif direction == "out":
            self.zoom_level -= self.zoom_amount

        self.zoom_level = max(0.1, min(2.0, self.zoom_level))

    def update_sprite_positions(self):
        for sprite in self.visible_sprites:
            # Calculate new position with zoom (using floating-point values)
            new_x = (sprite.rect.x + self.camera.x) * self.zoom_level
            new_y = (sprite.rect.y + self.camera.y) * self.zoom_level
            # Update sprite position (convert to integers for rendering)
            sprite.rect.topleft = int(new_x), int(new_y)
**
    def run(self):
        self.display = py.display.get_surface()

        #  Game-Loop
        while True:
            for event in py.event.get():
                if event.type == py.QUIT:
                    py.quit()
                    sys.exit()
                elif event.type == py.KEYDOWN:
                    if event.key == py.K_UP:
                        self.handle_zoom("in")
                    elif event.key == py.K_DOWN:
                        self.handle_zoom("out")

            **self.camera.x = -(self.player.rect.centerx * self.zoom_level - self.win_size[0] / 2)
            self.camera.y = -(self.player.rect.centery * self.zoom_level - self.win_size[1] / 2)
            print(f"Camera position: ({self.camera.x}, {self.camera.y}), Zoom level: {self.zoom_level}")**

            # Update sprite positions
            self.update_sprite_positions()
            #  Game Update
            self.visible_sprites.update()
            self.visible_sprites.draw(self.display)
            self.UI.display(self.player)
            py.display.update()
            self.clock.tick(self.FPS)


##########################################
# Die Klasse des Spielers
##########################################
class Player(py.sprite.Sprite):  # Wie sieht der Player aus?

    def __init__(self, pos, groups, create_weapon, destroy_weapon, create_magic):  # Hier ist der Bauplan des Players
        super().__init__(groups)  # Spieler zu beiden Gruppen adden
        self.display_surf = py.display.get_surface()

        #  Dictionaries für alle Animationen
        self.idle_frames = {
            "left": py.transform.scale(py.image.load("res/images/Player/Idle/Player_Left.png"), (TILESIZE, TILESIZE)),
            "right": py.transform.scale(py.image.load("res/images/Player/Idle/Player_Right.png"), (TILESIZE, TILESIZE)),
            "up": py.transform.scale(py.image.load("res/images/Player/Idle/Player_Up.png"), (TILESIZE, TILESIZE)),
            "down": py.transform.scale(py.image.load("res/images/Player/Idle/Player_Down.png"), (TILESIZE, TILESIZE)),
        }

        self.attack_frames = {
            "left": py.transform.scale(py.image.load("res/images/Player/Attack/Attack_Left.png"), (TILESIZE, TILESIZE)),

            "right": py.transform.scale(py.image.load("res/images/Player/Attack/Attack_Right.png"),
                                        (TILESIZE, TILESIZE)),

            "up": py.transform.scale(py.image.load("res/images/Player/Attack/Attack_Up.png"), (TILESIZE, TILESIZE)),

            "down": py.transform.scale(py.image.load("res/images/Player/Attack/Attack_Down.png"), (TILESIZE, TILESIZE)),

        }

        # Chat-GPT[

        self.walking_frames = {
            "left": [py.transform.scale(py.image.load(f"res/images/Player/Walk/Walk_Left/Walk_Left{i}.png"),
                                        (TILESIZE, TILESIZE)) for i in range(4)],

            "right": [py.transform.scale(py.image.load(f"res/images/Player/Walk/Walk_Right/Walk_Right{i}.png"),
                                         (TILESIZE, TILESIZE)) for i in range(4)],

            "up": [py.transform.scale(py.image.load(f"res/images/Player/Walk/Walk_Up/Walk_Up{i}.png"),
                                      (TILESIZE, TILESIZE)) for i in range(4)],

            "down": [py.transform.scale(py.image.load(f"res/images/Player/Walk/Walk_Down/Walk_Down{i}.png"),
                                        (TILESIZE, TILESIZE)) for i in range(4)],
        }
        # ]

        #  Richtungen Definieren
        self.idle_direction = "down"
        self.walk_direction = "down"
        self.attack_direction = "down"
        self.current_frame = 0

        # Auslesen
        self.image = self.idle_frames[self.idle_direction]
        self.rect = self.image.get_rect(topleft=pos)

        #  Waffen-Setup
        self.attacking = False
        self.attack_cooldown = 700
        self.attack_time = None
        self.create_weapon = create_weapon
        self.destroy_weapon = destroy_weapon
        self.weapon_index = 0
        self.weapon = list(weapon_type.keys())[self.weapon_index]  # Chat-GPT # Key für Waffen extrahieren + Liste
        # indexieren

        #  Magie-Setup
        self.create_magic = create_magic
        self.magic_index = 0
        self.magic = list(magic_type.keys())[self.magic_index]

        #  Deltas der Positionsänderungen
        self.x_change = 0
        self.y_change = 0

        # Player Stats definieren
        self.stats = {"health": 30, "stamina": 75, "magic": 30, "speed": 5, "max_health": 100, "max_magic": 40}
        self.health = self.stats["health"]
        self.stamina = self.stats["stamina"]
        self.speed = self.stats["speed"]

    def update_animation(self):

        # Attacken animation image zuschreiben
        if self.attacking:
            self.image = self.attack_frames[self.attack_direction]

        # Beim Stehen bleiben image zuschreiben
        elif self.x_change == 0 and self.y_change == 0:
            self.image = self.idle_frames[self.idle_direction]

        else:  # Chat-GPT [
            self.current_frame = (self.current_frame + 1) % (len(self.walking_frames[self.idle_direction]) * 5)
            # Increase the multiplier (5) to slow down more
            self.image = self.walking_frames[self.idle_direction][self.current_frame // 5]  # Divide by the multiplier
            # ]

    def get_keyboard_input(self):
        key = py.key.get_pressed()

        # Button-Inputs für die Bewegung überprüfen
        if not self.attacking:
            if key[py.K_a]:
                self.x_change -= self.speed
                self.attack_direction = "left"
                self.idle_direction = "left"
            if key[py.K_w]:
                self.y_change -= self.speed
                self.attack_direction = "up"
                self.idle_direction = "up"
            if key[py.K_d]:
                self.x_change += self.speed
                self.attack_direction = "right"
                self.idle_direction = "right"
            if key[py.K_s]:
                self.y_change += self.speed
                self.attack_direction = "down"
                self.idle_direction = "down"

            # Bei Angriff
            if key[py.K_l]:
                self.attacking = True
                self.attack_time = py.time.get_ticks()
                self.create_weapon()

            if key[py.K_k]:
                self.attacking = True
                self.attack_time = py.time.get_ticks()

                magic_kind = list(magic_type.keys())[self.magic_index]
                damage = list(magic_type.values())[self.magic_index]["damage"] + self.stats["magic"]
                stam = list(magic_type.values())[self.magic_index]["stam"]
                self.create_magic(magic_kind, damage, stam)

            #  Magie-Indexierung setzen
            if key[py.K_5]:
                self.magic_index = 0
            elif key[py.K_6]:
                self.magic_index = 1

            self.magic_index %= len(magic_type)
            magic_kind = list(magic_type.keys())[self.magic_index]

            # Waffen-Indexierung setzen
            if key[py.K_1]:
                self.weapon_index = 0
            elif key[py.K_2]:
                self.weapon_index = 1
            elif key[py.K_3]:
                self.weapon_index = 2
            elif key[py.K_4]:
                self.weapon_index = 3

            self.weapon_index %= len(weapon_type)
            self.weapon = list(weapon_type.keys())[self.weapon_index]

    def check_collisions(self, direction):
        if direction == "x":
            collision = py.sprite.spritecollide(self, Player.collisions_sprites, False)
            for collided_object in collision:
                if self.x_change > 0:  # Moving right
                    self.rect.right = collided_object.rect.left
                elif self.x_change < 0:  # Moving left
                    self.rect.left = collided_object.rect.right
        elif direction == "y":
            collision = py.sprite.spritecollide(self, Player.collisions_sprites, False)
            for collided_object in collision:
                if self.y_change > 0:  # Moving down
                    self.rect.bottom = collided_object.rect.top
                elif self.y_change < 0:  # Moving up
                    self.rect.top = collided_object.rect.bottom

    # Waffen cooldown setzen
    def cooldown(self):
        current_time = py.time.get_ticks()

        if self.attacking:
            if current_time - self.attack_time >= self.attack_cooldown:
                self.attacking = False
                self.destroy_weapon()

    def update(self):
        self.get_keyboard_input()
        self.cooldown()

        self.rect.x += self.x_change
        self.check_collisions("x")
        self.rect.y += self.y_change
        self.check_collisions("y")

        self.update_animation()

        self.x_change = 0
        self.y_change = 0


# Object erstellen
class Object(py.sprite.Sprite):
    def __init__(self, pos, groups, surf):
        super().__init__(groups)
        self.image = surf
self.rect = self.image.get_rect(topleft=pos)

game = Game()
game.create_map()
game.run()

Adjusting Camera Position: I attempted to adjust the camera position based on the zoom level to ensure that the player remains centered on the screen even after zooming in or out.

Updating Sprite Positions: I worked on updating the positions of sprites to accommodate the zoom level and camera position changes, aiming to ensure that they are rendered correctly on the screen at the right scale and position.

Handling Zoom Direction: I implemented logic to handle zooming in and out based on user input, ensuring that the zoom level remains within reasonable bounds to prevent any extreme scaling issues.

Refining Sprite Position Calculation: I refined the calculation of sprite positions after zooming to prevent them from being chopped or cut off on the screen, making adjustments to the positioning algorithm as necessary.

0

There are 0 best solutions below