Modellieren von Kräften ( Wind Gravitation)

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
hell
User
Beiträge: 40
Registriert: Montag 1. Oktober 2018, 18:01

Hallo liebe Forummitglieder,
ich versuche z.Z. gerade Programmbeispiele aus dem Buch Nature of Code von D. Shiffman auf Pygame und Python zu übertragen. (Kapitel 2)
Moverobjekte fallen durch Gravitation zu Boden und werden dabei durch Windkraft abgetrieben, sie prellen vom Boden ab und kommen dann zur Ruhe.
Shiffman verwendet Vektoren (PVector) für location ,velocity, usw., sowie für Kräfte, also habe ich pygame's Vector2 verwendet.
Die Methode apply_force in meinem Programm arbeitet auf einer Kopie des Kraftvektors, daher benötigte ich eine Hilfsmethode get_force, die eine
Kopie des Vektors zurückgibt.
Frage: kann ich das so machen, da ich in der Doku zu Vector2 keine copy-Methode gefunden habe
Noch eine Frage: Das Programm funktioniert , allerdings sollte es nicht ganz so funktionieren. Da keine Dämpfung auf die Objekte implementiert ist, sollten sie
unendlich lange weiter prellen, was sie aber nicht tun., denn sie kommen zur Ruhe. Was läuft hier grundsätzlich falsch.
Für Hilfe und Tipps wäre ich dankbar.

import pygame
import math
from random import randint, uniform
from copy import deepcopy
vec = pygame.math.Vector2

WIDTH = 1000
HEIGHT = 800
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
DARKGRAY = (40, 40, 40)
BG_COLOR = YELLOW


class Mover():
def __init__(self, position, size, m):
self.location = vec(0, 0)
self.location.x, self.location.y = position
self.velocity = vec(0,0)
self.acceleration = vec(0,0)
self.colour = BLUE
self.size = size
self.mass = m
self.thickness = 2


def get_force(self,force):
self.force = deepcopy(force)
return self.force

def apply_force(self,force):
f = self.get_force(force)
# 2.Newton Gesetz: Beschleunigung = Kraft / Masse
f /= self.mass
self.acceleration += f


def display(self):
pygame.draw.circle(screen, self.colour, (int(self.location.x),
int(self.location.y)), int(self.size)* int(self.mass), int(self.thickness))


def update(self):
self.velocity += self.acceleration
self.location += self.velocity
self.acceleration * 0.0 # resette nach jedem update


def check_edges(self):
#das passiert, wenn Ball Fenstergrenzen erreicht
if self.location.x > WIDTH-self.size:
self.location.x = WIDTH-self.size
self.velocity.x *= -0.8 # Dämpfung

elif self.location.x < self.size:
self.velocity.x *= -1
self.location.x = self.size

if self.location.y > HEIGHT:
self.velocity.y *= -1 # keine Dämpfung
self.location.y = HEIGHT


pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))

screen.fill(BG_COLOR)
clock = pygame.time.Clock()


# erzeuge zwei Moverobjekte
num_mover = 2
movers = []
for i in range(num_mover):
size = uniform(10, 30)
position = (uniform(size, WIDTH - size), size)
mass = uniform(1, 5)

mover = Mover(position,size, mass)
movers.append(mover)


running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False

screen.fill(BG_COLOR)

for mover in movers:
# Windkraft
wind = vec(0.001, 0)
# Gravitation, direkt mit Masse skaliert -> real gravity
m = mover.mass
gravity = vec(0, 0.004*m)


mover.apply_force(wind)
mover.apply_force(gravity)

mover.update()
mover.display()
mover.check_edges()

pygame.display.set_caption('Two Movers real gravity and wind')

pygame.display.flip()
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

schau dir mal ganz genau deine Methode update() an.

get_force() ist überflüssig

und apply_force() kannst du so schreiben

Code: Alles auswählen

def apply_force(self, force):
    # 2.Newton Gesetz: Beschleunigung = Kraft / Masse
    force /= self.mass
    self.acceleration += force
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
hell
User
Beiträge: 40
Registriert: Montag 1. Oktober 2018, 18:01

Danke Thomas L,
genau hier liegt laut Shiffman das Problem, er argumentiert so:
wir haben 2 Moveerobjekte m1 und m2 und wir haben eine Windkraft wind = vec2(1.0,0), und Masse mass = 10
m1 erhält die Windkraft : m1 equals wind force: (1,0)
Divided by mass of 10: (0.1,0)

m2 erhält ebenfalls die Windkraft: Frage: welchen Wert hat diese ? wind = vec2(0.1, 0) also den selben Wert wie m1.
But we don’t want m2 to receive
a force divided by the mass of object m1 . We want it to receive that force in its original
state—(1,0).
Also brauchen wir eine Kopie des Vektors: In processing:
void applyForce(PVector force) {
PVector f = force.get();
f.div(mass);
acceleration.add(f);
}

When you pass
an object (in this case a PVector ) into a function, you are passing a reference to that object.
It’s not a copy! So if a function makes a change to that object (which, in this case, it does by
dividing by mass) then that object is permanently changed!

In Englisch alles zitiert nach Shiffman Nature of Code S.72

Ehrlich gesagt, weiss ich wirklich nicht genau, ob das auf Python ebenso zutrifft.
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

per
mover.apply_force(wind)
mover.apply_force(gravity)
werden doch auf jedem mover object völlig unabhängig von einander die Kräfte angewendet.
Diese Wind-Force und Gravitation werden doch gar nicht modifiziert.
Die von dir zitierte Problematik hast du gar nicht.

und, hast du das fehlende Zeichen in update() entdeckt?
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
hell
User
Beiträge: 40
Registriert: Montag 1. Oktober 2018, 18:01

Thomas L
Hallo,
zu update() : da gehört ein Gleichheitszeichen hin: -> *=
damit ist meine zweite Ausgangsfrage bezüglich der fehlenden Dämpfung erledigt,
Danke!
Was allerdings deine Antwort auf die Verwendung einer Kopie des Vektors betrifft, so bin
ich noch nicht überzeugt. Der Einwand bei Shiffman erscheint mir logisch, allerdings bin ich
Anfänger, was python und pygame betrifft. Die von dir behauptete Unabhängigkeit bezüglich der Windkraft
möchtest du mir genauer erklären, weil ich das nicht verstehe.
Wenn ich 2 Objekte habe, dann geschieht doch
m1.apply_force(wind);
m2.apply_force(wind);

und in apply_force wird mit f /= mass der übergebene Windkraftvektor geändert
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

hell hat geschrieben: Sonntag 20. Januar 2019, 21:13 Wenn ich 2 Objekte habe, dann geschieht doch
m1.apply_force(wind);
m2.apply_force(wind);

und in apply_force wird mit f /= mass der übergebene Windkraftvektor geändert
bzgl. apply_force() hast du Recht, force wird geändert
das können wir so umgehen

Code: Alles auswählen

def apply_force(self, force):
    # 2.Newton Gesetz: Beschleunigung = Kraft / Masse
    self.acceleration += force / self.mass
aber du hast ja eine Schleife

Code: Alles auswählen

for mover in movers:
    wind = vec(0.001, 0)
    gravity = vec(0, 0.004 * mover.mass)
    mover.apply_force(wind)
    mover.apply_force(gravity)
und in jedem Schleifendurchlauf setzt du den Wert von wind und gravity neu.

Wenn wind ausserhalb der Schleife definiert wäre, würde deine Problematik greifen.
gravity muss ja innerhalb der Schleife berechnet werden.

Aber mit der geänderten apply_force() gibt es diese Problematik gar nicht mehr.
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
hell
User
Beiträge: 40
Registriert: Montag 1. Oktober 2018, 18:01

Danke ThomasL für deine schnellen Antworten,
mein Problem ist, dass ich processing code auf python/pygame übertragen will und deswegen klebe ich doch sehr an
dem processing code.
Dort wird ebenfalls im Hauptprogramm eine Schleife verwendet.
Das Problem scheint mir in apply_force zu liegen. In deiner Version arbeitest du nicht mit einer Kopie, wenn ich das recht verstehe.
Daher würden die Einwände von Shiffmann greifen und für einen frame würde Objekt 2 den Wert von Objekt 1 haben.
Allerdings sind meine Kenntnisse in python ebenso wie processing gering und ich kann nicht nachvollziehen, welche Werte
die einzelnen Kräfte haben. Also verlasse mich lieber auf deine Darstellung.
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Hallo Hell,
ich habe gerade noch mal schnell ins Buch geschaut https://natureofcode.com/book/chapter-2-forces/
und aus dem Ende von Kapitel 2.4 zitierst du die von ihm geschilderte Problematik, die man, wie Daniel selber schreibt, halt auf 2 Wegen lösen kann.

Der eine Weg ist der mit einer Kopie des Vektors, da nachfolgend die Vektormethode vector.div(skalar) verwendet wird und diese verändert halt den Vektor.
Der andere Weg, den ich in obigen Code verwende, ist die in Python umgesetzte Version der statische Methode Pvector.div(vector, skalar), die man ja dann in Aufgabe 2.2 verwenden soll.
Dadurch wird der als Parameter übergebene, konstante, Windvektor nicht verändert und könnte außerhalb der Schleife einmalig definiert werden.

Ich habe im übrigen vor einem Jahr genau das gleiche gemacht wie du und das ganze Buch und alle entsprechenden Youtube Folgen in Python codiert.
Bin darüber zu Neuronalen Netzen und Maschine Learning gekommen. Einfach genial.
Wenn du also im weiteren Verlauf Fragen hast, helfe ich gerne.
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Antworten