0

我正在尝试在 python 中实现一个相当基本的 boids 模拟。我的目标是模拟一个基本的捕食者猎物设置。我在这里找到了一些伪代码(不能发布两个以上的链接,但如果你用谷歌搜索,这是第一个结果boids pseudocode)和一些代码并决定试一试。因为我想添加捕食者,所以我决定将我找到的代码修改为精灵(将成为猎物),然后从那里开始。但是,我遇到了这个问题。

在我修改代码以使用 pygame sprites 后,所有的 boid 都移到了右下角(原始代码工作正常)。

我的代码(只是克隆 repo)在这里(github)。有没有人遇到过第一个问题?有没有人有任何想法来解决它?至于问题2,有人可以解释一下如何做到这一点吗?

谢谢你,任何帮助将不胜感激。

附言

除了它们总是移动到右下角这一事实之外,boid 的行为(它们的运动)似乎运行良好。

聚苯乙烯

多亏了 furas,猎物现在行为正常。

购买力平价

由于调试问题已经解决,我剩下的问题部分涉及解释,我认为应该是主题。

4

1 回答 1

0

您必须在代码中有所不同

  1. 您在开始时使用不同的速度速度 -__init__但这不应该有区别。

  2. 您在不同的时刻更新对象。

在所有计算之后,您同时移动所有猎物(使用组更新)。

原始代码在计算后移动每个 boid,因此下一个 boid 使用不同的数据来计算其移动。

我把prey.update()里面的for循环和删除all_sprites_list.update()

我以不同的方式组织代码:

#!/usr/bin/env python
# Prey implementation in Python using PyGame

from __future__ import division # required in Python 2.7

import sys
import pygame
import random
import math

# === constants === (UPPER_CASE names)

SCREEN_SIZE = SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600

BLACK = (0, 0, 0)

MAX_VELOCITY = 10
NUM_BOIDS = 50

BORDER = 25

# === classes === (CamelCase names for classes / lower_case names for method)

class Prey(pygame.sprite.Sprite):

    def __init__(self, x, y):
        super(Prey, self).__init__()

        # Load image as sprite
        self.image = pygame.image.load("ressources/img/prey.png").convert()

        # Fetch the rectangle object that has the dimensions of the image
        self.rect = self.image.get_rect()

        # Coordinates
        self.rect.x = x
        self.rect.y = y

#        self.velocityX = random.randint(-10, 10) / 10.0
#        self.velocityY = random.randint(-10, 10) / 10.0

        self.velocityX = random.randint(1, 10) / 10.0
        self.velocityY = random.randint(1, 10) / 10.0

    def distance(self, prey):
        '''Return the distance from another prey'''

        distX = self.rect.x - prey.rect.x
        distY = self.rect.y - prey.rect.y

        return math.sqrt(distX * distX + distY * distY)

    def move_closer(self, prey_list):
        '''Move closer to a set of prey_list'''

        if len(prey_list) < 1:
            return

        # calculate the average distances from the other prey_list
        avgX = 0
        avgY = 0
        for prey in prey_list:
            if prey.rect.x == self.rect.x and prey.rect.y == self.rect.y:
                continue

            avgX += (self.rect.x - prey.rect.x)
            avgY += (self.rect.y - prey.rect.y)

        avgX /= len(prey_list)
        avgY /= len(prey_list)

        # set our velocity towards the others
        distance = math.sqrt((avgX * avgX) + (avgY * avgY)) * -1.0

        self.velocityX -= (avgX / 100)
        self.velocityY -= (avgY / 100)


    def move_with(self, prey_list):
        '''Move with a set of prey_list'''

        if len(prey_list) < 1:
            return

        # calculate the average velocities of the other prey_list
        avgX = 0
        avgY = 0

        for prey in prey_list:
            avgX += prey.velocityX
            avgY += prey.velocityY

        avgX /= len(prey_list)
        avgY /= len(prey_list)

        # set our velocity towards the others
        self.velocityX += (avgX / 40)
        self.velocityY += (avgY / 40)

    def move_away(self, prey_list, minDistance):
        '''Move away from a set of prey_list. This avoids crowding'''

        if len(prey_list) < 1:
            return

        distanceX = 0
        distanceY = 0
        numClose = 0

        for prey in prey_list:
            distance = self.distance(prey)

            if  distance < minDistance:
                numClose += 1
                xdiff = (self.rect.x - prey.rect.x)
                ydiff = (self.rect.y - prey.rect.y)

                if xdiff >= 0:
                    xdiff = math.sqrt(minDistance) - xdiff
                elif xdiff < 0:
                    xdiff = -math.sqrt(minDistance) - xdiff

                if ydiff >= 0:
                    ydiff = math.sqrt(minDistance) - ydiff
                elif ydiff < 0:
                    ydiff = -math.sqrt(minDistance) - ydiff

                distanceX += xdiff
                distanceY += ydiff

        if numClose == 0:
            return

        self.velocityX -= distanceX / 5
        self.velocityY -= distanceY / 5

    def update(self):
        '''Perform actual movement based on our velocity'''

        if abs(self.velocityX) > MAX_VELOCITY or abs(self.velocityY) > MAX_VELOCITY:
            scaleFactor = MAX_VELOCITY / max(abs(self.velocityX), abs(self.velocityY))
            self.velocityX *= scaleFactor
            self.velocityY *= scaleFactor

        self.rect.x += self.velocityX
        self.rect.y += self.velocityY

# === main === (lower_case names)

# --- init ---

pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
#screen_rect = screen.get_rect()

# --- objects ---

# lists
# This is a list of 'sprites.' Each block in the program is
# added to this list. The list is managed by a class called 'Group.'
prey_list = pygame.sprite.Group()

# This is a list of every sprite. All blocks and the player block as well.
all_sprites_list = pygame.sprite.Group()

# create prey_list at random positions
for i in range(NUM_BOIDS):
    prey = Prey(random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT))
    # Add the prey to the list of objects
    prey_list.add(prey)
    all_sprites_list.add(prey)

# --- mainloop ---

clock = pygame.time.Clock()

running = True

while running:

    # --- events ---

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False

    # --- updates ---

    for prey in prey_list:
        closeBoids = []
        for otherBoid in prey_list:
            if otherBoid == prey:
                continue
            distance = prey.distance(otherBoid)
            if distance < 200:
                closeBoids.append(otherBoid)

        prey.move_closer(closeBoids)
        prey.move_with(closeBoids)
        prey.move_away(closeBoids, 20)

        # ensure they stay within the screen space
        # if we roubound we can lose some of our velocity
        if prey.rect.x < BORDER and prey.velocityX < 0:
            prey.velocityX = -prey.velocityX * random.random()
        if prey.rect.x > SCREEN_WIDTH - BORDER and prey.velocityX > 0:
            prey.velocityX = -prey.velocityX * random.random()
        if prey.rect.y < BORDER and prey.velocityY < 0:
            prey.velocityY = -prey.velocityY * random.random()
        if prey.rect.y > SCREEN_HEIGHT - BORDER and prey.velocityY > 0:
            prey.velocityY = -prey.velocityY * random.random()

        prey.update()

    # Calls update() method on every sprite in the list
    #all_sprites_list.update()

    # --- draws ---

    screen.fill(BLACK)

    # Draw all the spites
    all_sprites_list.draw(screen)

    # Go ahead and update the screen with what we've drawn.
    pygame.display.flip()

    # Limit to 60 frames per second
    # Used to manage how fast the screen updates
    clock.tick(120)

# --- the end ---
pygame.quit()
sys.exit()

编辑:真正的问题是在 Python 2 中将两个整数相除,结果四舍五入为整数

解决方案:

from __future__ import division

在其他导入之前使用它。

于 2016-02-01T03:48:28.930 回答