Steering

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

Code: Alles auswählen

[code]Hallo Menschen aus dem Forum,
nach längerer Abstinenz mit pygame hatte ich Lust, mich mit dem Thema Steering Behavior (Reynolds) zu beschäftigen, genauer den altenCode durch den Code, den mir _blackjack__  zukommen ließ umzuschreiben. Dafür noch mal vielen Dank. Die Vehicleklasse ist immer noch "Gottklasse" (--blackjack__), leider. Für die Zukunft würde ich schon ganz gerne verstehen, wie ich das alles mit dem Strategy_Pattern lösen kann. Aber zunächst habe ich noch ein kleineres Problem mit der  update-Methode der Vehicleklasse:
Code:
 def update(self):
        
        if self.speed > 0.00001:
            self.image = pygame.transform.rotate(
                self.base_image, self.direction.angle_to(Vector2(1, 0))
            )
        
        self.velocity.scale_to_length(self.max_speed) # kommentiere ein,  
                                                      # um seek(mouse) laufen zu lassen
        self.velocity += self.acceleration
        
        # begrenze velocity auf MAX_SPEED
        if self.speed > self.max_speed:
            self.velocity.scale_to_length(self.max_speed)
            
        
        self.location += self.velocity
        self.acceleration *= 0.0 # resette nach jedem update
        
        self.rect = (
           self.image.get_rect(center=self.location)
          
        )
        # ff Code macht, dass das rect-Objekt innerhalb der 
        # Screenkoordinaten bleibt:
        # self.image.get_rect(center=self.location).clamp(0, 0, WIDTH, HEIGHT))
         
Wenn ich die so laufen lasse in Programmen, wie seek(mouse) oder wo seek(target) indirekt vorkommt, wie follow_path usw, so schiesst das Fahrzeug über das Ziel hinaus und verschwindet im Nirwana.
Programm laufen jedoch korrekt, wenn die Codesequenz self.velocity.scale_to_length(self.max_speed) einkommentiert ist.
Andererseits, wenn eine Programm wie avoid_obstacles läuft, so ist diese Codesequenz unbedingt erforderlich, denn sonst wird das Fahrzeug nach Kontakten mit dem Hindernissen immer langsamer und kommt dann zur Ruhe. Leider bin ich ein wenig perfektionistisch und will das nicht so hinnehmen. Meine Frage ist: gibt es eine Möglichkeit, die update-Methode der Vehicleklasse wo zu schreiben, dass sie für alle Steeringstrategien läuft.
Oder ist da ein Fehler drin, den ich übersehen habe.
Hier dann doch noch das gesamte Programm:


import math
from random import randint, uniform

import pygame
from pygame.math import Vector2

WIDTH = 1200
HEIGHT = 880
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)
CYAN = (0, 255, 255)
DARKGRAY = (40, 40, 40)
BG_COLOR = (150, 150, 80)

FLEE_DISTANCE = 200
LOOK_AHEAD = 80

MAX_AVOID_FORCE = 0.005
APPROACH_RADIUS = 50
NUM_OBSTACLE = 20
WALL_LIMIT = 40
OBSTACLE_RADIUS = 70

obstacles = []

AGENT_FILENAME = '../images/dreieck0.png'
#AGENT_FILENAME1 = '../images/dreieck0.png'
#AGENT_FILENAME2 = '../images/dreieck0.png'
#AGENT_FILENAME3 = '../images/dreieck0.png'
#
# TODO Bessere einzelnamen oder in eine Liste stecken.
#
screen = pygame.display.set_mode((WIDTH, HEIGHT))

def calculate_non_overlapping_circles():
""" Kreise stellen Hindernisse dar """
protection = 0

for _ in range(500):
# zufällige Positonen generieren
location_x = randint(OBSTACLE_RADIUS, WIDTH - OBSTACLE_RADIUS)
location_y = randint(OBSTACLE_RADIUS, HEIGHT - OBSTACLE_RADIUS)
obstacle = Obstacle(location_x, location_y, OBSTACLE_RADIUS)

overlapping = False

# teste alle positionen
for i in range(len(obstacles)):
obstacle = Obstacle(location_x, location_y, obstacle.radius)
other = Obstacle(location_x, location_y, OBSTACLE_RADIUS)
other = obstacles

# überschneiden sich die Kreise
if obstacle.overlaps(other):
overlapping = True

# falls sich Kreise nicht überschneiden
# füge diese zur Liste hinzu
if not overlapping:
obstacles.append(obstacle)
return obstacles

protection += 1
if protection > 1000:
break


def line_intersects_obstacle(obstacle, start_point, end_point):
return (
obstacle.location.distance_to(start_point) <= obstacle.radius
or obstacle.location.distance_to(end_point) <= obstacle.radius
)


def draw_walls():
#limits
r = pygame.Rect(WALL_LIMIT, WALL_LIMIT, WIDTH - WALL_LIMIT * 2, HEIGHT - WALL_LIMIT * 2)
pygame.draw.rect(screen, WHITE, r, 5)


class Obstacle:

def __init__(self, x, y, radius):
self.location = Vector2(x, y)
self.radius = radius

def distance_to(self, other):
return self.location.distance_to(other.location)

def overlaps(self, other):
return self.distance_to(other) < uniform(0.9, 3.5)* (self.radius + other.radius)

def draw(self, screen):
"""Hindernisse werden als Kreis dargestellt."""
pygame.draw.circle(
screen,
CYAN,
(int(self.location.x), int(self.location.y)),
int(self.radius-20),
)


class Vehicle:

def __init__(self, image_filename, init_position, max_speed, max_force):
self.base_image = pygame.image.load(image_filename).convert_alpha()
self.image = self.base_image
self.location = Vector2(init_position)
self.rect = self.image.get_rect(center=self.location)
self.max_speed = max_speed
self.velocity = Vector2(self.max_speed, self.max_speed)
#
# TODO Attribute ab hier gehören eigentlich nicht mehr allgemein zu
# einem Fahrzeug, sondern zu den Strategien/Algorithmen um ein
# Fahrzeug zu steuern, und das sollte sauber ausgelagert werden.
#
# Durch Vererbung oder besser Komposition, zum Beispiel mit dem
# „Strategie“-Entwurfsmuster.
#
self.max_force = max_force
self.acceleration = Vector2()
self.wander_angle = 0
self.displacement = Vector2()

@property
def speed(self):
return self.velocity.length()

@property
def direction(self):
return self.velocity.normalize()

def seek(self, target):
desired = (target - self.location).normalize() * self.max_speed
self.apply_force(desired - self.velocity)

def seek_with_approach(self, target):
""" nach Bucklands arrive-Algorithmus """
deceleration = 5
to_target = target - self.location
# berechne die Entfernung zum target
distance = to_target.length()
if distance > 0:
deceleration_tweaker = 5
# calculate the speed required to reach the target given the desired
# deceleration
speed = distance / (deceleration * deceleration_tweaker)
#
# make sure the velocity does not exceed the max
#
speed = min(speed, self.max_speed)
#
# from here proceed just like Seek except we don't need to normalize
# the ToTarget vector because we have already gone to the trouble
# of calculating its length: distance.
#
desired = to_target * speed / distance
self.apply_force(desired - self.velocity)


def avoid_walls(self):
if self.location.x < WALL_LIMIT:
desired = Vector2(self.max_speed, self.velocity.y)
elif self.location.x > WIDTH - WALL_LIMIT:
desired = Vector2(-self.max_speed, self.velocity.y)
elif self.location.y < WALL_LIMIT:
desired = Vector2(self.velocity.x, self.max_speed)
elif self.location.y > HEIGHT - WALL_LIMIT:
desired = Vector2(self.velocity.x, -self.max_speed)
else:
desired = Vector2()

if desired:
self.apply_force(desired - self.velocity)


def find_most_threatening(self, obstacles, ahead, ahead2):
most_threatening = None
for obstacle in obstacles:
collision = line_intersects_obstacle(obstacle, ahead, ahead2)

if collision and (not most_threatening or
self.location.distance_to(obstacle.location) <
self.location.distance_to(most_threatening.location)):
most_threatening = obstacle
return most_threatening



def collision_avoidance(self,obstacle):
steer = Vector2()
ahead = self.location + self.velocity.normalize() * LOOK_AHEAD

ahead2 = self.location + self.velocity.normalize() * LOOK_AHEAD * 0.5

most_threatening = self.find_most_threatening(obstacles, ahead, ahead2)
if most_threatening:
steer = ahead - most_threatening.location
steer.normalize() * MAX_AVOID_FORCE # statt normalize_ip

self.apply_force(steer)

def apply_force(self, force):
if force.length() > self.max_force:
force.scale_to_length(self.max_force)
self.acceleration += force


def update(self):

if self.speed > 0.00001:
self.image = pygame.transform.rotate(
self.base_image, self.direction.angle_to(Vector2(1, 0))
)

self.velocity.scale_to_length(self.max_speed) # kommentiere ein,
# um seek(mouse) laufen zu lassen
self.velocity += self.acceleration

# begrenze velocity auf MAX_SPEED
#if self.acceleration.length() > 0.000001: #ist hier unbedingt notwendig
# self.velocity.scale_to_length(self.max_speed)
if self.speed > self.max_speed:
self.velocity.scale_to_length(self.max_speed)


self.location += self.velocity
self.acceleration *= 0.0 # resette nach jedem update

self.rect = (
self.image.get_rect(center=self.location)

)
# ff Code macht, dass das rect-Objekt innerhalb der
# Screenkoordinaten bleibt:
# self.image.get_rect(center=self.location).clamp(0, 0, WIDTH, HEIGHT))

def draw(self, screen):
screen.blit(self.image, self.rect)

########################### main ######################
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Obstacle Avoidance None Overlapping Circles')
clock = pygame.time.Clock()


agent = Vehicle(
AGENT_FILENAME,
(randint(0, WIDTH), randint(0, HEIGHT)),
4.0,
0.3,
)

#agent1 = Vehicle(
# AGENT_FILENAME1,
# (randint(0, WIDTH), randint(0, HEIGHT)),
# 2.0,
# 0.050,
#)

#agent2 = Vehicle(
# AGENT_FILENAME2,
# (randint(0, WIDTH), randint(0, HEIGHT)),
# 2.0,
# 0.15,
#)

#agent3 = Vehicle(
# AGENT_FILENAME3,
# (randint(0, WIDTH), randint(0, HEIGHT)),
# 2.0,
# 0.30,
#)


# erzeuge Hindernisse
for _ in range(NUM_OBSTACLE):
# Aufruf der Funktion
calculate_non_overlapping_circles()

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

screen.fill(BG_COLOR)

mouse_pos = pygame.mouse.get_pos()
mouse = Vector2(mouse_pos)



for obstacle in obstacles:
obstacle.draw(screen)

draw_walls()

#agent.seek_with_approach(mouse)
agent.update()
agent.collision_avoidance(obstacle)
agent.avoid_walls()
agent.draw(screen)

#agent1.update()
#agent1.collision_avoidance(obstacle)
#agent1.avoid_walls()
#agent1.draw(screen)

#agent2.update()
#agent2.collision_avoidance(obstacle)
#agent2.avoid_walls()
#agent2.draw(screen)

#agent3.update()
#agent3.collision_avoidance(obstacle)
#agent3.avoid_walls()
#agent3.draw(screen)

pygame.display.flip()


if __name__ == '__main__':
main()
[/code]

Vielleicht kann mir jmd. helfen?
Gruss hell
Antworten