Seite 1 von 1

nochmal Modelieren von Kräften

Verfasst: Montag 28. Januar 2019, 17:04
von hell
Hallo liebe Forumsmitglieder,
Programm animiert zwei Moverobjekte, auf die Gravitation und Reibung wirken.
Um Reibungskraft zu animieren bin ich so vorgegangen:
Reibung = -1 * C * N * (Einheitsvektor Geschwindigkeit) mit C: Reibungsfaktor
N: Normalkraft ( Wert 1 gesetzt, da nur Animation)
als Code: friction = mover.get_velocity()
friction = friction.normalize() * -1 * C
und dann mit apply_force(friction) den Kräften hinzugefügt
Bekomme jedoch diese Fehlermeldung: friction = friction.normalize() * -1 * C
ValueError: Can't normalize Vector of length Zero

Was auch völlig korrekt ist, da zu Beginn der Animation die Geschwindigkeit (0, 0) ist.
Weil ich nicht weiss, wie man diesen Wert des velocity- Vektors abfangen kann , habe ich einfach
Phantasiewerte für diesen Fall angenommen mit:
if friction[0] == 0.0 or friction[1] == 0.0:
friction[0], friction[1] = .001, .001
Das Programm läuft dann zwar, aber nicht korrekt, denn wenn ich C = 0.0 setze müssten die Moverobjekte unendlich weiter vom Boden abprallen.
Was sie aber nicht tun.
Vielleicht kann mir jemand helfen?

Code:
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,50)
DARKGRAY = (40, 40, 40)
BG_COLOR = YELLOW
# Reibungsfaktor
C = 0.1


class Mover():
def __init__(self, position, 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.mass = m
self.thickness = 2


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

def get_velocity(self):
return self.velocity

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

def display(self):
pygame.draw.circle(screen, self.colour, (int(self.location.x),
int(self.location.y)), 8*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.location.x = WIDTH
self.velocity.x *= -1 # keine Dämpfung

elif self.location.x < 0:
self.location.x = 0
self.velocity.x *= -1 # keine Dämpfung

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
m1 = Mover((300, 0), 1)
m2 = Mover((500, 0), 5)
movers = [m1, m2]


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.09, 0)

# Gravitation, direkt mit Masse skaliert -> real gravity
#m = mover.mass
gravity = vec(0, 0.5*mover.mass)

# Reibungskraft
friction = mover.get_velocity()
print(friction) # debug

# um unmögliche Berechnung der Normalisierung der Anfangsgeschwindigkeit
# velocity = (0,0) zu umgehen, auf willkürliche Werte festgesetzt
if friction[0] == 0.0 or friction[1] == 0.0:

friction[0], friction[1] = .001, .001
# normalisiere Vektor, skaliere in Gegenrichtung und skaliere mit
# Reibungsfaktor
friction = friction.normalize() * -1 * C
#friction *= -1
#friction = friction.normalize() * C

mover.apply_force(friction)
mover.apply_force(wind)
mover.apply_force(gravity)
mover.update()
mover.display()
mover.check_edges()

pygame.display.set_caption('Movers friction')

pygame.display.flip()

Re: nochmal Modelieren von Kräften

Verfasst: Montag 28. Januar 2019, 17:28
von __deets__
Bitte deinen Code in die dafuer vorgesehenen Code-Tags setzen, damit er hier lesbar ist. So gehen die Einrueckungen verloren.

Und bezueglich deines Problems: wenn du keine Geschwindigkeit hast, hast du auch keine Reibung. Statt also einen Fantasiewert zu nehmen, solltest du den Null-Vektor nehmen. Oder im Zweifel noch besser einfach den ValueError abfangen, und dann eben keinen Reibungsvektor addieren - das ist dann ja im Ergebnis gleich zum addieren des Nullvektors.

Re: nochmal Modelieren von Kräften

Verfasst: Montag 28. Januar 2019, 17:36
von ThomasL
Hallo hell,
ein Vektor wird normalisiert, in dem er durch seine Länge/Magnitude geteilt wird.
Wenn diese gleich Null ist, geht das natürlich nicht.
Also vorher den Vektor prüfen, ob seine Länge größer Null ist:
(PS: die Kopie des velocity Vektors friction zu nennen ist nicht gut )

Code: Alles auswählen

# Reibungskraft
velocity = mover.get_velocity()
if velocity.length() > 0:
    # normalisiere Vektor, skaliere in Gegenrichtung und skaliere mit Reibungsfaktor
    friction = velocity.normalize() * -1 * C
    mover.apply_force(friction)

Re: nochmal Modelieren von Kräften

Verfasst: Montag 28. Januar 2019, 18:01
von Sirius3
Klassen schreibt groß und Abkürzungen vermeiden, also statt `vec` einfach `Vector2` direkt importieren und benutzen. Warum erzeugst Du einen 0-Vektor um ihn dann mit position zu überschreiben? -> `location = Vector2(position)´
`get_force` für ein `set_force` zu schreiben ist verwirrend. Eigentlich ist dieser Setter gar nicht nötig, weil man auch direkt auf x.force zugreifen kann, und ob man eine Kopie braucht, sollte der Setzer bestimmen. `get_velocity` ist ein unnötiger Getter. Warum übergibt man `apply_force` `force`, wenn man doch ein `force` als Attribut hat, bzw. umgekehrt, warum gibt es überhaupt ein force-Attribut?
draw.circle kann auch direkt mit einem Vector arbeiten.
Alles ab `pygame.init` sollte in einer Funktion stehen, die üblicherweise `main` heißt.

Re: nochmal Modelieren von Kräften

Verfasst: Montag 28. Januar 2019, 18:19
von ThomasL
Sirius3 hat geschrieben: Montag 28. Januar 2019, 18:01 Warum übergibt man `apply_force` `force`, wenn man doch ein `force` als Attribut hat, bzw. umgekehrt, warum gibt es überhaupt ein force-Attribut?
Die Methode get_force(self,force) und das Attribut self.force sind obsolet, ich vermute mal der OP hell hat vergessen, diese Codezeilen zu löschen.
Stammen noch aus "alten" Zeiten.

Re: nochmal Modelieren von Kräften

Verfasst: Montag 28. Januar 2019, 19:05
von hell
Danke Thomas L.
ist eigentlich ganz einfach, aber als Anfänger ....


Hallo Sirius , danke für deine Antwort,
ich versuche gerade Processing code (Shiffman , Nature of Code) in Python und Pygame zu übertragen, um dadurch etwas mehr Python , Pygame und OOPzu lernen.

"draw.circle kann auch direkt mit einem Vector arbeiten." verstehe ich nicht so recht. Wenn ich Shiffman recht verstanden habe, so wird der Zustand des Moverobjekts über die Methode apply_force(force) geändert, die display()-Methode soll nur das Objekt zeichnen.
Bitte,bitte habe Geduld mit mir.

Re: nochmal Modelieren von Kräften

Verfasst: Montag 28. Januar 2019, 19:53
von ThomasL
Sirius3 hat geschrieben: Montag 28. Januar 2019, 18:01 draw.circle kann auch direkt mit einem Vector arbeiten.
Das geht nicht, da Pygame für die x,y Koordinaten integer erwartet und der location Vektor aus floats besteht.