nochmal Modelieren von Kräften

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 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()
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

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.
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

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)
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
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

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.
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

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.
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.
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.
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

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.
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