Spritesheet richtig laden/ausrichten

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
Pebender
User
Beiträge: 2
Registriert: Dienstag 20. Dezember 2022, 06:02

Hallo und guten Abend,

dies ist mein erster Post, als absoluter Python-Anfänger und gleich eine Frage.....
ich möchte mich an einem Programm versuchen, sagen wir mal blöde im Darkorbit-Style ich habe in der Mitte meines Bildschirms ein Raumschiff, das sich zum Mauszeiger hin drehen soll, bzw, der Hintergrund soll sich nachher so verschieben, das es aussieht, als würde das schiff darüberfliegen.....aber das ist noch in der Ferne.
ich bin eigentlich eher umsteiger von der Sprache Blitzmax zu Python....
mein Problem, und ja ich habe schon gegoogelt und irgendwie nicht das gefunden, wonach ich suche....
ich habe ein Spritesheet mit 10 frames in der Reihe und 6 in einer Spalte, sprich 60 frames....
ich lade den Hintergrund und die Sprites ein, platziere das oder die Sprites in der Mitte, bis hierhin soweit gut....
jetzt dreht sich das frame mit dem mauszeiger, zwar noch ncht so, wie es soll, aber erstmal Nebensache....
das Sprite ist trotz allem irgendwie nicht richtig zentriert, beim drehen "eiert" es herum, wie bekomm ich das weg ??
hier mal mein Test-Code.....

Code: Alles auswählen

import pygame
import math
# Initialisieren Sie Pygame und erstellen Sie ein Fenster
pygame.init()
screen = pygame.display.set_mode((800, 600))

fps = 60
bg_x = 0    #variable für den background
bg_y = 0    #variable für den background
background = pygame.image.load("c:\midimaster/spacebg3_green.jpg")
# Laden Sie das Spritesheet als Bild und extrahieren Sie das erste sprite
spritesheet = pygame.image.load("c:\midimaster/ship.png")
sprite_width = spritesheet.get_width() // 10
sprite_height = spritesheet.get_height() // 6
sprite = spritesheet.subsurface(pygame.Rect(0, 0, sprite_width, sprite_height))
clock = pygame.time.Clock()

def rotieren_zentrieren(x, y, bild, gradangabe):
    # rotieren und in einem neuen "surface" speichern
    rotiert = pygame.transform.rotate(spritesheet, angle)

    # Bestimmen der neuen Abmessungen (nach Rotation ändern sich diese!)
    groesse = rotiert.get_rect()


    # Viereck zur Kontrolle zeichnen


def ship_rotate():
    mouse_x, mouse_y = pygame.mouse.get_pos()
    sprite_x = 400
    sprite_y = 300
    angle = math.degrees(math.atan2(mouse_y - sprite_y, mouse_x - sprite_x))
    rotated_sprite = pygame.transform.rotate(sprite, angle)
    screen.blit(rotated_sprite, (sprite_x - sprite_width // 2, sprite_y - sprite_height // 2))


running = True
while running:
    # Überprüfen Sie, ob der Benutzer das Fenster geschlossen hat
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    screen.fill((0, 0, 0))


    # Aktualisieren Sie das Fenster
    screen.blit(background, (bg_x, bg_y))
    ship_rotate()
    pygame.display.flip()
    clock.tick(fps)

# Beenden Sie Pygame, wenn das Spiel beendet ist
pygame.quit()
wie gesagt, seid behutsam, Anfänger XD

mfg Peter
Benutzeravatar
__blackjack__
User
Beiträge: 11588
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Pebender: `sprite_width` und `sprite_height` beziehen sich auf die unrotierte Grafik. Wenn man rotiert, ändert sich die Grösse. Das steht doch in den Kommentaren der `rotieren_zentrieren()`-Funktion. Das muss man kompensieren. Am einfachsten in dem man die Position über den Mittelpunkt des `Rect`-Objektes bestimmt. Also statt mit den festen Werten der nicht rotierten Grafik, mit den tatsächlichen Werten des `Rect` der rotierten Grafik. Und man muss da auch nicht selber rechnen, man sagt einfach das man das `Rect` haben will und welche Koordinaten den Mittelpunkt festlegen sollen. Das kann man der `get_rect()`-Methode schon sagen.

Anmerkungen zum Quelltext:

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Besonders unübersichtlich ist es wenn man Funktionsdefinitionen mitten in anderen Code schreibt.

Vor Funktionen und Klassen werden Konstanten definiert, und die werden KOMPLETT_GROSS geschrieben, damit man sie als Konstanten erkennen kann und nicht in Versuchung kommt den Wert doch mal zu ändern. `bg_x` und `bg_y` scheinen in dem Sinne auch eher Konstanten zu sein.

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Der Name soll dem Leser vermitteln was der Wert dahinter im Programm bedeutet, nicht zum rätseln zwingen. Wenn man `bg_x` nicht kryptisch abkürzt, braucht man auch keinen Kommentar der erklärt das `bg` für „background“ steht.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

Daten wie (Teil)Pfade sollte man nicht wiederholen, sondern als Konstante(n) festlegen. Dann lässt sich das einfacher und unfallfreier ändern, ohne das man den gesamten Quelltext durchgehen muss.

Auch ”magische” Zahlen sollte man als Konstanten definieren. Beispielsweise die Sprites pro Zeile und wie viele Zeilen das Spritesheet hat. Und Zahlen die von anderen Zahlen abhängen sollte man nicht absolut in den Quelltext schreiben. Die Spriteposition scheint ja die Mitte vom `screen` zu sein — das sollte man dann auch so ausdrücken und nicht als absolute Zahlen die automatisch falsch werden wenn man nur die Fenstergrösse ändert.

Der \ hat eine besondere Bedeutung in literalen Zeichenketten. Das "\m" *keine* besondere Bedeutung hat, ist hier also vielleicht nur Glück. Man sollte die *immer* escapen, oder ”rohe” Zeichenkettenliterale verwenden, oder auch unter Windows in Pfaden den / als Trenner.

`ship_rotate()` ist als Name unzureichend weil die Funktion mehr macht. Die zeichnet das Schiff auch gleich noch. Ist also eher eine `draw_rotated_ship()`. Und dazu braucht sie das Surface auf das gezeichnet wird, und sie braucht auch das Schiff. Alles was eine Funktion (oder Methode) ausser Konstanten benötigt, bekommt sie als Argument(e) übergeben.

Pygame hat einen praktischen Vektor-Datentyp, den man für die Berechnung des Winkels verwenden kann.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
from pathlib import Path

import pygame

BLACK = (0, 0, 0)

WINDOW_SIZE = (800, 600)
FRAMES_PER_SECOND = 60

BASE_PATH = Path("C:/midimaster")

BACKGROUND_IMAGE_PATH = BASE_PATH / "spacebg3_green.jpg"
BACKGROUND_POSITION = (0, 0)
SHIP_SPRITESHEET_PATH = BASE_PATH / "ship.png"
SHIP_SPRITES_PER_ROW = 10
SHIP_SPRITE_ROWS = 6


def draw_rotated_ship(surface, ship_image):
    mouse_position = pygame.Vector2(pygame.mouse.get_pos())
    center = pygame.Vector2(surface.get_rect().center)
    angle = (mouse_position - center).angle_to((0, 1))
    rotated_image = pygame.transform.rotate(ship_image, angle)
    surface.blit(rotated_image, rotated_image.get_rect(center=center))


def main():
    try:
        pygame.init()
        screen = pygame.display.set_mode(WINDOW_SIZE)
        background = pygame.image.load(BACKGROUND_IMAGE_PATH)
        spritesheet = pygame.image.load(SHIP_SPRITESHEET_PATH)
        sprite = spritesheet.subsurface(
            pygame.Rect(
                0,
                0,
                spritesheet.get_width() // SHIP_SPRITES_PER_ROW,
                spritesheet.get_height() // SHIP_SPRITE_ROWS,
            )
        )
        clock = pygame.time.Clock()
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return

            screen.fill(BLACK)
            screen.blit(background, BACKGROUND_POSITION)
            draw_rotated_ship(screen, sprite)
            pygame.display.flip()
            clock.tick(FRAMES_PER_SECOND)

    finally:
        pygame.quit()


if __name__ == "__main__":
    main()
“The gods of the Disc have never bothered much about judging the souls of the dead, and so people only go to hell if that's where they think they deserve to go. Which they won't do if they don't know about it. This explains why it is important to shoot missionaries on sight.” -Terry Pratchett, Eric
Pebender
User
Beiträge: 2
Registriert: Dienstag 20. Dezember 2022, 06:02

Hallo und vielen Dank,

ich werde mir das ganze zu Herzen nehmen und versuchen mich an deine Tipps zu halten..

mfg Peter
Antworten