Pfadsteuerung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
blueWolf
User
Beiträge: 4
Registriert: Montag 27. Juni 2016, 21:42

Hallo liebe Community,

ich bin in den Python-Gewässern relativ neu und kenne nocht nicht ganz das Ausmaß dieser neuen Welt.

Was ich machen will: Ich möchte per Toutch einen Pfad zeichnen und entlang diesen Pfades ein Objekt bzw. Schiff bewegen.

Was ich bis jetzt probiert habe:
Erster Ansatz:
Ich zeichne meinen Pfad und dessen Koordinaten werden in einer Liste abgespeichert. Des Weiteren wird anhand dieser Koordinaten ein Pfad auf dem Display visuell mitgezeichnet. Beim Absetzen des Fingers wird der letzte Knoten mit dem aller ersten wieder verbunden, so dass ich immer einen geschlossenen Pfad habe, und mein Objekt setzt sich in Bewegung. Für die Bewegung des Objekts berechne ich Vektoren zwischen dem aktuellen und folgenden Eintrag und multipliziere es mit der Updaterate. So weit, so gut. Das Problem ist, dass ich eine absolut inkonstante Geschwindigkeit meines Objekts habe und meine Liste nicht gerade klein ist. Wenn also mehrere Punkte dicht beieinander liegen, dann müssen sehr viele Vektoren berechnet werden und das zeigt sich an der Geschwindigkeit des Objekts sehr deutlich. Ich konnte dieses Problem teils lösen; indem ich einfach gesagt habe, dass eine Koordinate erst eingetragen wird, wenn sie zur vorherigen eine bestimmte Distanz erfüllt. Mit dieser Variante habe ich zwar meine Liste gedrittelt oder geviertelt, aber mein Objekt bewegt sich trotzdem sehr ungleichmäßig.

Zweiter Ansatz: Meine 2. Überlegung war nun; aus diesem Pfad eine Funktion zu machen. Der Gedanke war, dass ich daurch sowohl einen eleganten Pfad (nicht mehr eckig) als auch eine konstante Geschwindigkeit erhalte. Diesen Gedanken habe ich nun mit B-Splines umgesetzt. Ich nutze meine Liste als Stützstellen, interpoliere etc. Der Pfad sieht nun hüpsch aus. Des Weiteren musste ich aber feststellen, dass meine Einträge für x und y der Funktion 2000 Elemente zusammen erreichen und ich befürchte, dass es vielleicht auf die Performance auswirken könnte, da ich an die 5 - 10 solcher Pfade haben werde. Und nun das große Problem bei dieser Variante: Ich habe keinen Plan, wie ich mein Objekt entlang einer Funktion bewegen kann. Ich habe etwas rumprobiert - erfolglos.

Frage: Denke ich wieder zu kompliziert? Das passiert bei mir des Öfteren. Habt ihr vielleicht eine Idee zur Lösung dieses speziellen Problems?

LG blueWolf
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Per Touch geht in Python schonmal gar nichts. Python ist nur eine Programmiersprache. Du setzt hier eine riesige Anzahl von Dingen voraus, von Hardware, Betriebssystem, Grafik Stack bis hin Frameworks ohne auch nur anzudeuten worum es konkret gehen könnte. Du erwartest doch nicht ernsthaft dass irgendjemand in der Lage ist eine brauchbare Antwort zu bieten ohne diesen Kontext zu haben?
BlackJack

Zwischenfrage: Wieso sollte sich die Anzahl der Punkte (so deutlich) auf die Leistung auswirken? Im ersten Fall betrachtest Du doch immer nur zwei Punkte für den aktuellen Streckenabschnitt, im zweiten je nach Spline ein paar mehr, aber immer nur konstant viele. Egal wie viele Punkte den gesamten Pfad ausmachen.

Da die Frage doch eher allgemein getellt ist, eine allgemeine Antwort: Schau Dir an wie Freihandzeichnen und die Funktion zum vereinfachen von Pfaden in Vektorgrafikprogrammen gelöst wird. In Inkscape beispielsweise.
blueWolf
User
Beiträge: 4
Registriert: Montag 27. Juni 2016, 21:42

Ich bedanke mich erst mal für die Antworten.
DasIch hat geschrieben:Per Touch geht in Python schonmal gar nichts. Python ist nur eine Programmiersprache. Du setzt hier eine riesige Anzahl von Dingen voraus, von Hardware, Betriebssystem, Grafik Stack bis hin Frameworks ohne auch nur anzudeuten worum es konkret gehen könnte. Du erwartest doch nicht ernsthaft dass irgendjemand in der Lage ist eine brauchbare Antwort zu bieten ohne diesen Kontext zu haben?
Ist es von Belangen, welches Framwork ich zur Erkennung von Touch bzw. Mausbewegung verwende? Mein Problem besteht darin, dass ich nicht genau weiß wie ich etwas entlang einer Kurve bewegen kann. Dabei ging es mir eher um das allgemeine Prinzip dahinter und mit welchen Mitteln ich es vielleicht umsetzen könnte. Es ja etwas anderes ein Objekt entlang einer Funktion, als entlang eines Vektors, zu bewegen, oder liege ich da falsch? Des Weiteren habe ich mein B-Spline mit NumPy und SciPy umgesetzt.
BlackJack hat geschrieben:Zwischenfrage: Wieso sollte sich die Anzahl der Punkte (so deutlich) auf die Leistung auswirken? Im ersten Fall betrachtest Du doch immer nur zwei Punkte für den aktuellen Streckenabschnitt, im zweiten je nach Spline ein paar mehr, aber immer nur konstant viele. Egal wie viele Punkte den gesamten Pfad ausmachen.
Ich habe gelesen, dass Python Performance-Probleme, ähnlich wie Java, hat. Und wenn ich jetzt an die 10 Listen mit über 2000 Einträgen/Liste habe, weiß ich nicht genau wie schnell Python an seine Grenzen kommen könnte. Es ist aber eher eine hypothetische Frage, weil es sein könnte, dass ich mehrfach durch diese Listen gehen muss. Diese Frage hängt aber von der Umsetzung der Bewegung entlang einer Funktion ab. Derzeit ist es so, dass ich mit jedem Frame, wird von meinem Framwork verwaltet, einen Counter mitlaufen lasse und einzelne Elemente aus der Liste nehme.

Tut mir Leid, wenn ich mich nicht deutlich ausdrücke bzw. meinen Standpunkt nicht klarmachen kann. Ich bin sehr selten in solchen Foren, als Verfasser, unterwegs und weiß nicht genau, was alles an Informationen notwendig ist. Ich will hier auch keine Romane schreiben.

LG blueWolf
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@blueWolf: Du hast hoffentlich keine Listen sondern numpy-Arrays, bei denen die Performance stimmen sollte. Wenn Du schon etwas mit Touch-Gesten probiert hast, dann hast Du Dir ja schon irgendein Framework ausgesucht. Du scheinst mit Deiner Umsetzung auch schon recht weit zu sein, und Dein Problem sehr konkret. Dann hilft es enorm, wenn Du den passenden Sourcecode postest und anhand dessen Dein Problem schilderst.
Benutzeravatar
noisefloor
User
Beiträge: 4253
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Mein Problem besteht darin, dass ich nicht genau weiß wie ich etwas entlang einer Kurve bewegen kann.
Axo. Ich hatte dein 1. Posting auch so verstanden, dass du Probleme mit dem Zeichnung und der Berechnung der Kurve hast - und das mit der Bewegung ein mögliches Problem der Zukunft ist.

Wenn du die Kurve hast musst du das Objekt doch "nur" von Punkt zu Punkt der Kurve bewegen? Wenn du die Punkte nicht hast, weil die Kurve z.B. durch eine Funktion beschrieben hast, musst du die Punkte halt mit der gewünschten Auflösung / Dichte berechnen.
Und wenn ich jetzt an die 10 Listen mit über 2000 Einträgen/Liste habe, weiß ich nicht genau wie schnell Python an seine Grenzen kommen könnte.
Mit so wenig Daten? Gar nicht, das wuppt selbst eine schwaches System wie ein Raspi ohne Problem. 10 Listen mit 2000 Einträgen sind geschätzt nur ein paar Dutzend kB Daten.

Du kannst den Speicherbedarf von Objekten aber auch mit Bordmitteln abfragen:

Code: Alles auswählen

>>> import sys
>>> my_list = [1, 2, 3]
>>> sys.getsizeof(my_list)
88
Gruß, noisefloor
BlackJack

@noisefloor: Wobei das jetzt nur die grösse der Liste ist. Die enthaltenen Objekte sind da nicht mit eingerechnet. Und da wird es dann auch schwierig. Wie Rechnet man da Objekte die vielleicht auch woanders verwendet werden mit rein.
blueWolf
User
Beiträge: 4
Registriert: Montag 27. Juni 2016, 21:42

@noisefloor: Hehe, okay, also brauch' ich mir um so etwas keine Gedanken machen. :)

Hier meine erste Umsetzung:

Code: Alles auswählen

def __onMouseDown(self, event):
	...
        # Hier wird die 1. Koordinate des Cursors an die Liste übergeben
        self.__pos_list.append((self.__image.x, self.__image.y))
        self.__start_pos = self.__image.pos
        ...
        
def __onMouseMove(self, event):
	...
	    # Hier berechne ich die Distanz zwischen dem letzten Eintrag in der Liste und der aktuellen Position des Cursors
	    # ist die Länge > 20, dann wirde die Position des Cursors in die Liste eingetragen
            if math.hypot(node.x - self.__pos_list[len(self.__pos_list) - 1][0], node.y - self.__pos_list[len(self.__pos_list) - 1][1]) > 20:
                self.__pos_list.append((node.x, node.y))
                ...

def __onMouseUp(self, event):
	...
	    # Beim absetzen wird der Startpunkt noch einmal in die Liste eingetragen, damit ich immer einen geschlossenen Pfad habe
            self.__pos_list.append((self.__pos_list[0]))
	    ...
            self.__start()
            ...

def __start(self):
        self.__list_index = 0
        self.calcVec = True
        self.r_vector = 0
        self.vector = None
        
        # Hier werden die Frames gehollt - dies verwaltet das Framework
        self.__frameHandlerId = player.subscribe(player.ON_FRAME, self.__onFrame)
  
def __onFrame(self):
      	...
            if self.calcVec:
            	# Berechnung des Vectors
                self.vector = (self.__pos_list[self.__list_index + 1][0] - self.__pos_list[self.__list_index][0], self.__pos_list[self.__list_index + 1][1] - 	self.__pos_list[self.__list_index][1])
                # Berechnung der Distanz von 2 Punkten
                self.r_vector = math.hypot(self.vector[0], self.vector[1])
                self.calcVec = False

            if math.hypot(self.__pos_list[self.__list_index][0] - self.__image.x, self.__pos_list[self.__list_index][1] - self.__image.y) <= self.r_vector:
                perc = 0 # Speed
                # Hier hatte ich versucht die Geschwindigkeit etwas zu regeln
                if len(self.__pos_list) - 2 == self.__list_index:
                    perc = 0.001
                else:
                    perc = 0.01
                    
                dt = player.getFrameDuration() * perc
                # Hier wird letztendlich das Bild/Objekt bewegt
                self.__image.x += self.vector[0] * dt
                self.__image.y += self.vector[1] * dt
             
            else:
                if len(self.__pos_list) > self.__list_index + 2:
                    self.calcVec = True
                    self.__list_index += 1
                else:
                    self.__image.pos = self.__start_pos
                    self.__list_index = 0
                    self.calcVec = True

Das Resultat in Bild: Bild

Das Problem hierbei, ist, dass ich eine inkonstante Geschwindigkeit habe, da er immer anhält, um den nächsten Vektor zu berechnen. Im Klartext bedeutet es, wenn ich einen sehr großen Vektor habe, dann ist die Bewegung sehr schnell und wenn ich mehrere kurze Vektoren habe, dann ist die Bewegung sehr langsam. Den Fall mit den vielen kleinen Vektoren habe ich oben ausgeschlossen, da Punkte nur mit einer bestimmten Distanz eingetragen werden. So habe ich zwar einen Stillstand ausgeschlossen, aber die ungleichmäßige Geschwindigkeit bleibt.

Nun die 2. Variante mit B-Spline. Ehrlich gesagt habe ich diese Funktion nicht selbst geschrieben (bei stackoverflow geklaut ^-^), da ich noch am testen bin und nicht viel Zeit in etwas investieren wollte, was schon vorhanden ist - ist aber natürlich auf meinen Fall zugeschnitten.

Code: Alles auswählen

    def bspline(self):
	# line_list ist meine Liste an Koordinaten des Pfades
        points = line_list
        degree = 3

        points = points + points[0:degree + 1]
        points = np.array(points)
        x = points[:, 0]
        y = points[:, 1]

        t = range(len(x))
        # Calculate query range
        ipl_t = np.linspace(1.0, len(points) - degree, 1000)

        x_tup = si.splrep(t, x, k=degree, per=1)
        y_tup = si.splrep(t, y, k=degree, per=1)

        x_list = list(x_tup)
        xl = x.tolist()
        x_list[1] = xl

        y_list = list(y_tup)
        yl = y.tolist()
        y_list[1] = yl

        x_i = si.splev(ipl_t, x_list)
        y_i = si.splev(ipl_t, y_list)
        
        # Ab hier ist mein B-Spline fertig und kann weiterverwendet werden.
       ...
Das Resultat: Blauer Pfad ist mein Spline.
Bild

Das Problem ist jetzt, dass ich mein Schiff nicht so bewegen kann, wie im 1. Fall. Die Punkte der Funktion sind so dicht beieinander, dass ich absoluten Stillstand des Schiffs hervorrufe, wenn ich die Vektoren der einzelnen Punkte berechne.
Zuletzt geändert von Anonymous am Mittwoch 29. Juni 2016, 09:33, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Benutzeravatar
Kebap
User
Beiträge: 786
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

blueWolf hat geschrieben: Tut mir Leid, wenn ich mich nicht deutlich ausdrücke bzw. meinen Standpunkt nicht klarmachen kann. Ich bin sehr selten in solchen Foren, als Verfasser, unterwegs und weiß nicht genau, was alles an Informationen notwendig ist. Ich will hier auch keine Romane schreiben.
Das ist löblich. Hier gibt es gesammelte Ratschläge zu dieser Frage, die tatsächlich recht häufig auftaucht: Wie man Fragen richtig stellt. Übrigens willkommen im Forum und bei Python! :)
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
BlackJack

@blueWolf: Um eine konstante Geschwindigkeit zu erreichen, könnte man die Gesamtlänge des Pfades ermitteln und speichern von wo bis wo welcher Teilpfad (Vektor oder Spline) geht. Dann bewegst Du das Schiff auf diesem Gesamtpfad. Also aus dem aktuellen Punkt auf dem Gesamtpfad das Teilstück ermitteln und dann wo relativ zu dessen Anfang und Ende sich das Schiff befindet. Dann ist auch egal wie dicht zusammen oder weit auseinander die Punkte liegen die Du mit der Maus festgelegt hast.
blueWolf
User
Beiträge: 4
Registriert: Montag 27. Juni 2016, 21:42

Hallo, da bin ich wieder. :)

@Kebap
Hehe, da gehe ich wohl zu 100% als LOOSER durch. :D
Danke für den informativen Link.
Danke für den Willkommensgruß. :)

@BlackJack
Ich muss mich bei dir besonders bedanken. Ich hatte mir von meinem Vorhaben etwas Abstand genommen gehabt und beim späteren Lesen deines Posts, den darauffolgenden, etwaigen Überlegungen und Tests bin ich letztendlich auf die Fehlerquelle all meiner Probleme gestoßen, welche ich die letzten Tage zu lösen versuchte und es nun geschafft habe.
BlackJack hat geschrieben:@blueWolf: Um eine konstante Geschwindigkeit zu erreichen, könnte man die Gesamtlänge des Pfades ermitteln und speichern von wo bis wo welcher Teilpfad (Vektor oder Spline) geht. Dann bewegst Du das Schiff auf diesem Gesamtpfad. Also aus dem aktuellen Punkt auf dem Gesamtpfad das Teilstück ermitteln und dann wo relativ zu dessen Anfang und Ende sich das Schiff befindet. Dann ist auch egal wie dicht zusammen oder weit auseinander die Punkte liegen die Du mit der Maus festgelegt hast.
Ich hatte es ja so ähnlich gehabt. Ich hatte meinen Vektor aufgestellt gehabt, die Länge bestimmt, das Schiff bewegt und geschaut, wo sich das Shiff auf dem Vektor befindet. Genau da lag auch das Problem, also in der Bewegung. Er hatte bei der Bewegung, obwohl es theoretisch richtig war, manchmal zu viel drauf addiert, wodurch sich das Schiff zu weit eintfernt hatte. Am Ende war das Schiff mit einer einzigen falschen Berechnung z.b. an die 20 Einheiten entfernt gewesen, obwohl die Länge des eigentlichen Vektors nur vielleicht 2 Einheiten betrug. Daraus folgte, dass, je nachdem wie ich einen Pfad gezeichnet hatte, mindestens 500 von 1000 Einträgen in der Liste übersprungen wurden.

Meine Lösug dieses Problems ist nun, dass ich meine Punke des b-splines in gleichgroße Vektoren aufteile und das Schiff von einem zum nächsten Punkt pro Frame springen lasse. Funktionier perfekt.

Mein Fazit dieser ganzen Geschichte:
~ ab und zu Abstand nehmen
~ eine weitere Variable, die das Zwischenergebnis für weitere Berechnungen spreichert, ist goldwert
~ auch der theoretisch richtige Code (kann) ist in der Regel falsch (sein)
~ Leute nicht in Foren belästigen, wenn man nicht das explizite Problem kennt :)
Antworten