Pygame对象仅在鼠标点击时移动

时间:2016-02-03 15:34:27

标签: python python-2.7 pygame

我有一些简单的圆圈(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()

1 个答案:

答案 0 :(得分:0)

这是因为您在

中进行了所有计算
elif event.type == pygame.MOUSEBUTTONDOWN:

MOUSEBUTTONDOWN表示该按钮会将状态从UP更改为DOWN(并且需要很短的时间)。这并不意味着button is holding pressed all the time

如果您需要查看天气button is holding pressed,请使用pygame.mouse.get_pressed(),但在for event循环之后/之后使用。

它类似于关键事件:

enter image description here