Pygame: Rotieren von Sprites in Richtung Mausklick

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
Lony
User
Beiträge: 1
Registriert: Freitag 1. Januar 2016, 19:40

Hallo alle :),

Ich versuche ein Panzerspiel mit Python/Pygame zu programmieren. Dabei sollen die Panzer zum Mausklick "fahren". Jedoch bekomme ich ein Problem mit dem rotieren von Sprites. Ich möchte, dass sich die Sprites zuerst zum Mausklick ausrichten und erst dann fahren.
Ich weiss, wie ich den Winkel von Sprite zum Mausklick berechnen soll, aber eine Drehung mit gegebener Winkel scheitert bei mir schon.
Ich hoffe ihr könnt mir weiterhelfen. Habe schon überall gegoogelt :P.
Gruss
Lony

Code: Alles auswählen

import pygame
import math
import random
pygame.init()

white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)


def load_image(name, image_widht, image_height):  # Funktion um Bilder zu laden und skalieren
    image = pygame.image.load(name)
    image = pygame.transform.scale(image, (image_widht, image_height))
    return image


class Vector():
    """
        Klasse:
            erzeugt Operationen um mit Vektoren umzugehen
        """
    def __init__(self, x, y):  # Initialisiert
        self.x = x
        self.y = y

    def __getitem__(self, key):
        if key == 0:
            return self.x
        elif key == 1:
            return self.y
        else:
            raise IndexError("This "+str(key)+" key is not a vector key!")

    def __sub__(self, o):  # Subtraktion zwischen Vektoren
        return Vector(self.x - o.x, self.y - o.y)

    def length(self):  # berechnet die Laenge eines Vektors
        return math.sqrt((self.x**2 + self.y**2))

    def normalize(self):  # berechnet den Einheitsvektor; Richtung bleibt
        l = self.length()
        if l != 0:
            return (self.x / l, self.y / l)
        return None


class PanzerSprite(pygame.sprite.Sprite):
    """
    Klasse:
        erzeugt Panzerbilder als Sprite (erbt von pygame.sprite.Sprite)
        transformiert sie
        berrechnet die Laengen zum Ziel
        und laesst das Sprite sich bewegen
    """

    def __init__(self):
        pygame.sprite.Sprite.__init__(self)  # die Methoden der Elternklasse werden geladen
        self.image = load_image("Panzer1.0.png", 20, 40)  # Bild wird geladen und verkleinert/vergroessert
        self.rect = self.image.get_rect()
        self.blickrichtung = 0

        self.trueX = random.randint(0, 800)
        self.trueY = random.randint(0, 600)
        self.rect.center = (self.trueX, self.trueY)  # Startposition wird gesetzt;
                                                     # Das Zentrum des Bildes ist auf dem Punkt
        self.speed = 4  # Geschwindigkeit des Panzers
        self.rotatespeed = 4  # Geschwindigkeit des Rotierens
        self.target = None  # Erstes Ziel des Panzers
        self.angle = None  # Erster Winkel


    def get_direction(self, target):  # Der Richtungsvektor wird berechnet

        if self.target:
            position = Vector(self.rect.centerx, self.rect.centery)  # Ortsvektor vom Panzer
            target = Vector(target[0], target[1])  # Ortsvektor vom Ziel des Panzers
            self.dist = target - position  # Vektor zwischen Panzer und Ziel

            direction = self.dist.normalize()  # Einheitsvektor, welche Richtung
            return direction


    def distance_check(self, dist):

        dist_x = dist[0] ** 2
        dist_y = dist[1] ** 2
        t_dist = dist_x + dist_y
        speed = self.speed ** 2

        if t_dist < (speed):
            return True

        """
    def rot_center(self, angle): # Methode um das Sprite zu rotieren
        # rotate an image while keeping its center and size
        orig_rect = self.image.get_rect()
        rot_image = pygame.transform.rotate(self.image, angle)
        rot_rect = orig_rect.copy()
        rot_rect.center = rot_image.get_rect().center
        rot_image = rot_image.subsurface(rot_rect).copy()
        self.image = rot_image
        return self.image
        """

    def update(self):
        # --------Panzer fahrt----------------
        self.dir = self.get_direction(self.target)
        if self.dir:

            # self.rot_center(angle)   # hier sollte die Rotationsmethode aufgerufen werden, angle z.B. 45°

            if self.distance_check(self.dist):
                self.rect.center = self.target

            else:
                self.trueX += (self.dir[0] * self.speed)
                self.trueY += (self.dir[1] * self.speed)
                self.rect.center = (round(self.trueX), round(self.trueY))



# erzeugt Panzer
panzer = PanzerSprite()
panzer2= PanzerSprite()

# Liste fuer alle Panzer
Panzer_list = pygame.sprite.Group()

Panzer_list.add(panzer,panzer2)# fuegt einen Panzer in die Liste der Panzerobjekte


def main():
    gameDisplay = pygame.display.set_mode((800,600))
    pygame.display.set_caption("2D Panzerspiel")
    pygame.mouse.set_visible(1)
    pygame.key.set_repeat(30)
    clock = pygame.time.Clock()

    gameExit = False
    while not gameExit:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                for i in Panzer_list:  # fuer jeden Panzer in Panzerliste
                    i.target = event.pos  #setzt Mausklick als Ziel
                    print i.target

        gameDisplay.fill(white)

        Panzer_list.draw(gameDisplay)  #alle Panzer werden auf die Surface gameDisplay gezeichnet
        for i in Panzer_list:  # jeder Panzer in Panzerliste
            i.update()  # wird die Methode update() angewendet


        pygame.display.update()  # Frame fuer Frame wird geladen

        clock.tick(30)  # Frames pro Sekunde wird gesetzt


main()
Zuletzt geändert von Anonymous am Freitag 1. Januar 2016, 20:39, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@Lony: Du müsstest den `Vector` noch um eine Methode erweitern die den Winkel berechnet. Den vergleichst Du dann mit dem Blickwinkel bzw. dem Richtungsvektor der Panzerbewegung. Falls die Winkel (deutlich) voneinander abweichen, muss sich der Panzer in dem Schritt zum Ziel ausrichten statt zu fahren. Dazu müsstest Du berechnen in welche Richtung er sich weniger drehen müsste und dann ein Stück in diese Richtung drehen. Maximal so viel bis beide Winkel (annähernd) gleich sind.

Anmerkungen zum Quelltext: Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm sollte komplett in der `main()`-Funktion stehen.

Konstanten werden per Konvention komplett in Grossbuchstaben geschrieben.

In DocStrings für eine Klasse muss man nicht jedesmal mit "Klasse:" anfangen. Dass das eine Klasse ist, sieht man ja schon am ``class``-Schlüsselwort in der Zeile davor. Ausserdem sieht man es am Namen der Klasse, denn nur Klassennamen werden per Konvention in `MixedCase` geschrieben.

Kommentare sollten dem Leser einen Mehrwert über den Code liefern und nicht noch einmal das beschreiben was im Code offensichtlich schon einmal steht. Bei den Kommentaren zu den Methoden ist zu überlegen ob das nicht besser DocStrings wären.

Bei `Vector.__getitem__()` ist der Name des Arguments falsch. Es handelt sich hier offensichtlich um einen Index und nicht um einen Schlüssel. Falls der Wert unpassend ist, löst Du korrekterweise ja auch einen `IndexError` und keinen `KeyError` aus.

Zusammenstückeln von Zeichenketten und Werten mittels `str()` und ``+`` ist eher BASIC als Python. In Python gibt es dafür Zeichenkettenformatierung mittels `format()`-Methode.

Wenn der `Vector` auch als Sequenz und Werttyp benutzt werden soll, böte es sich an ihn von einem `collections.namedtupel`-Exemplar abzuleiten.

Einbuchstabige Namen sind in der Regel keine guten Namen. Ausnahmen sind `i`, `j`, `k` für Indices oder `x`, `y`, `z` für Koordinaten. Oder Namen deren Gültigkeit auf einen Ausdruck beschränkt sind, beispielsweise in einem Generatorausdruck oder einem ``lambda``-Ausdruck.

An die `PanzerSprite`-Exemplare werden zu viele Attribute gebunden die dort nicht hingehören weil sie eigentlich nur lokale Werte sind. In der `__init__()` sollten zudem alle Attribute eingeführt werden. Wenn man in beliebigen Methoden neue Attribute definiert, wird das Programm sehr unübersichtlich und die Benutzbarkeit des Objekts hängt von der Reihenfolge ab in der die Methoden aufgerufen werden.
Antworten