我有一些简单的圆圈(boids)以模拟鸟类的方式移动;他们应该避免彼此过于接近,同时保持相同的总标题等。
我正在使用pygame,但除非我按下GUI中的一个按钮,否则圆圈不会移动,这看起来很奇怪,但我无法弄清楚我搞砸了哪里。
代码中最相关的部分可能是gui函数和Boid类中的draw函数。
import pygame
import numpy as np
import sys
import math
class BoidWorld:
# Boid movement parameters
w_separation = 10
w_alignment = 1
w_cohesion = 1
w_avoidance = 0
w_flee = 50
dim = 0 # dim*dim = Size of world
neighbour_radius = 100
max_velocity = 100
# Objects in world
boids = []
predators = []
obstacles = []
def __init__(self, dim):
self.dim = dim
def update_boid_velocity(self, boid):
# Flee from predators, if any
predator = self.get_predator(boid)
flee_x, flee_y = self.calc_flee_force(boid, predator)
# Avoid obstacles, if any
obstacle = self.get_obstacle(boid)
avoid_x, avoid_y = self.calc_avoidance_force(boid, obstacle)
# Get neighbours within radius r
neighbours = self.get_neighbours(boid)
sep_x, sep_y = self.calc_separation_force(boid, neighbours)
align_x, align_y = self.calc_alignment_force(neighbours)
coh_x, coh_y = self.calc_cohesion_force(neighbours)
boid.velocity_x += self.w_separation * sep_x + self.w_alignment * align_x + self.w_cohesion * coh_x + \
self.w_avoidance * avoid_x + self.w_flee * flee_x
boid.velocity_y += self.w_separation * sep_y + self.w_alignment * align_y + self.w_cohesion * coh_y + \
self.w_avoidance * avoid_y + self.w_flee * flee_y
# Limit velocity by creating unit vectors and multiplying by max velocity
v = math.sqrt(boid.velocity_x**2 + boid.velocity_y**2)
if v > self.max_velocity:
boid.velocity_x = boid.velocity_x*self.max_velocity/v
boid.velocity_y = boid.velocity_y*self.max_velocity/v
boid.position_x += boid.velocity_x
boid.position_y += boid.velocity_y
print(boid.velocity_x, boid.velocity_y)
# Wrap around
if boid.position_x > self.dim or boid.position_x < 0:
boid.position_x %= self.dim
if boid.position_y > self.dim or boid.position_y < 0:
boid.position_y %= self.dim
def update_predator_velocity(self, predator):
pass
def calc_separation_force(self, boid, neighbours):
sep_x = 0.
sep_y = 0.
for b in neighbours:
sep_x = sep_x - (b.position_x - boid.position_x)
sep_y = sep_y - (b.position_y - boid.position_y)
return sep_x, sep_y
def calc_alignment_force(self, neighbours):
if not neighbours: return 0, 0
avg_heading_x = 0.
avg_heading_y = 0.
for b in neighbours:
avg_heading_x += b.velocity_x
avg_heading_y += b.velocity_y
return avg_heading_x/len(neighbours), avg_heading_y/len(neighbours)
def calc_cohesion_force(self, neighbours):
if not neighbours: return 0, 0
avg_pos_x = 0.
avg_pos_y = 0.
for b in neighbours:
avg_pos_x += b.position_x
avg_pos_y += b.position_y
return avg_pos_x/len(neighbours), avg_pos_y/len(neighbours)
# Flee straight away from predators
def calc_flee_force(self, boid, predator):
if not predator: return 0
return boid.position - predator.position
# Avoid obstacles
def calc_avoidance_force(self, boid, obstacle):
if not obstacle: return 0
return 0
# Predators chasing boids
def calc_chasing_force(self, predator, boids):
return 0
def get_predator(self, boid):
for predator in self.predators:
if self.is_neighbour(predator, boid):
return predator
return None
def get_obstacle(self, boid):
for obstacle in self.obstacles:
if self.is_neighbour(obstacle, boid):
return obstacle
return None
def is_neighbour(self, boid1, boid2):
if np.power(boid2.position_x - boid1.position_x, 2) + \
np.power(boid2.position_y - boid1.position_y, 2) \
< np.power(self.neighbour_radius, 2):
return True
return False
def get_neighbours(self, boid):
neighbours = []
for b in self.boids:
if b != boid and self.is_neighbour(b, boid):
neighbours.append(b)
return neighbours
def add_boid(self):
self.boids.append(Boid(
self.rand_position(), self.rand_position(),
self.rand_velocity(), self.rand_velocity()
))
def add_obstacle(self):
self.obstacles.append(Obstacle(
self.rand_position(), self.rand_position()))
def add_predator(self):
self.predators.append(Predator(
self.rand_position(), self.rand_position(),
self.rand_velocity(), self.rand_velocity()
))
def remove_boids(self):
self.boids = []
def remove_obstacles(self):
self.obstacles = []
def remove_predators(self):
self.predators = []
def rand_position(self):
return float(np.random.randint(0, self.dim))
def rand_velocity(self):
return float(np.random.randint(0, self.max_velocity))
class Boid(object):
color_circle = (100, 0, 0)
color_line = (100, 0, 100)
radius = 10
position_x = 0.
position_y = 0.
velocity_x = 0.
velocity_y = 0.
def __init__(self, position_x, position_y, velocity_x, velocity_y):
self.position_x = position_x
self.position_y = position_y
self.velocity_x = velocity_x
self.velocity_y = velocity_y
def draw(self, screen):
pygame.draw.circle(screen, self.color_circle, (int(round(self.position_x)), int(round(self.position_y))),
self.radius, 0)
# Velocity vector
pygame.draw.lines(screen, self.color_line, False, [
(int(round(self.position_x)), int(round(self.position_y))),
(int(round(self.position_x+self.velocity_x)), int(round(self.position_y+self.velocity_y)))
], 2)
class Predator(Boid):
color_circle = (100, 55, 0)
color_line = (100, 0, 100)
radius = 20
class Obstacle:
color = (0, 33, 50)
position_x = 0.
position_y = 0.
radius = 15
def __init__(self, position_x, position_y):
self.position_x = position_x
self.position_y = position_y
def draw(self, screen):
pygame.draw.circle(screen, self.color, (int(round(self.position_x)), int(round(self.position_y))),
self.radius, 0)
def main():
pygame.init()
boid_world = BoidWorld(800)
boid_world.add_boid()
gui(boid_world)
def gui(boid_world):
weight_inc = 0.1
btn_boid_add = Button('Add boid')
btn_boid_rem = Button('Remove boids')
btn_obst_add = Button('Add obstacle')
btn_obst_rem = Button('Remove obstacles')
btn_pred_add = Button('Add predator')
btn_pred_rem = Button('Remove predators')
btn_sep_p = Button('+')
btn_sep_m = Button('-')
btn_ali_p = Button('+')
btn_ali_m = Button('-')
btn_coh_p = Button('+')
btn_coh_m = Button('-')
pygame.font.init()
font = pygame.font.Font(None, 20)
font_color = (255, 255, 255)
screen = pygame.display.set_mode((1200, 800))
screen_half = screen.subsurface((400, 0, 800, 800))
pygame.display.set_caption('Boids')
clock = pygame.time.Clock()
run = True
while run:
screen.fill((0, 0, 0))
mouse = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if btn_boid_add.obj.collidepoint(mouse):
boid_world.add_boid()
elif btn_boid_rem.obj.collidepoint(mouse):
boid_world.remove_boids()
elif btn_obst_add.obj.collidepoint(mouse):
boid_world.add_obstacle()
elif btn_obst_rem.obj.collidepoint(mouse):
boid_world.remove_obstacles()
elif btn_pred_add.obj.collidepoint(mouse):
boid_world.add_predator()
elif btn_pred_rem.obj.collidepoint(mouse):
boid_world.remove_predators()
elif btn_sep_m.obj.collidepoint(mouse):
boid_world.w_separation -= weight_inc
elif btn_sep_p.obj.collidepoint(mouse):
boid_world.w_separation += weight_inc
elif btn_ali_p.obj.collidepoint(mouse):
boid_world.w_alignment -= weight_inc
elif btn_ali_m.obj.collidepoint(mouse):
boid_world.w_alignment += weight_inc
elif btn_coh_m.obj.collidepoint(mouse):
boid_world.w_cohesion -= weight_inc
elif btn_coh_p.obj.collidepoint(mouse):
boid_world.w_cohesion += weight_inc
btn_boid_add.draw(screen, mouse, (10, 10, 100, 20), (15, 15))
btn_boid_rem.draw(screen, mouse, (120, 10, 130, 20), (125, 15))
btn_obst_add.draw(screen, mouse, (10, 40, 100, 20), (15, 45))
btn_obst_rem.draw(screen, mouse, (120, 40, 130, 20), (125, 45))
btn_pred_add.draw(screen, mouse, (10, 70, 100, 20), (15, 75))
btn_pred_rem.draw(screen, mouse, (120, 70, 130, 20), (125, 75))
btn_sep_m.draw(screen, mouse, (120, 100, 20, 20), (125, 105))
btn_sep_p.draw(screen, mouse, (150, 100, 20, 20), (155, 105))
btn_ali_m.draw(screen, mouse, (120, 130, 20, 20), (125, 135))
btn_ali_p.draw(screen, mouse, (150, 130, 20, 20), (155, 135))
btn_coh_m.draw(screen, mouse, (120, 160, 20, 20), (125, 165))
btn_coh_p.draw(screen, mouse, (150, 160, 20, 20), (155, 165))
screen.blit(font.render('Separation', 1, font_color), (15, 105))
screen.blit(font.render('Alignment', 1, font_color), (15, 135))
screen.blit(font.render('Cohesion', 1, font_color), (15, 165))
for boid in boid_world.boids:
boid_world.update_boid_velocity(boid)
boid.draw(screen_half)
for obstacle in boid_world.obstacles:
obstacle.draw(screen_half)
for predator in boid_world.predators:
boid_world.update_predator_velocity(predator)
predator.draw(screen_half)
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
class Button:
def __init__(self, text):
self.text = text
self.is_hover = False
self.default_color = (100, 100, 100)
self.hover_color = (255, 255, 255)
self.font_color = (100, 0, 0)
self.obj = None
def label(self):
font = pygame.font.Font(None, 20)
return font.render(self.text, 1, self.font_color)
def color(self):
if self.is_hover:
return self.hover_color
else:
return self.default_color
def draw(self, screen, mouse, rectcoord, labelcoord):
# create rect obj, draw, and change color based on input
self.obj = pygame.draw.rect(screen, self.color(), rectcoord)
screen.blit(self.label(), labelcoord)
# change color if mouse over button
self.check_hover(mouse)
def check_hover(self, mouse):
# adjust is_hover value based on mouse over button - to change hover color
if self.obj.collidepoint(mouse):
self.is_hover = True
else:
self.is_hover = False
if __name__ == "__main__":
main()