Why do the balls behave the way they do?

2019-07-03 23:26发布

问题:

I want each ball to move independently. I think the problem has something to do with them all having the same velocity, but I don't know why they do or if that's even the problem. Also, why does the right portion of the screen behave the way it does? I want the balls to be able to move all over the screen properly.

import sys
import pygame
import random

screen_size = (screen_x, screen_y) = (640, 480)
screen = pygame.display.set_mode(screen_size)

size = {"width": 10, "height": 10}
velocity = {"x": {"mag": random.randint(3,7), "dir": random.randrange(-1,2,2)}, "y": {"mag": random.randint(3,7), "dir": random.randrange(-1,2,2)}}


class Ball(object):
    def __init__(self, size, position, velocity):
        self.size = size
        self.position = position
        self.velocity = velocity
        self.color = (255, 255, 255)

    def update(self):
        self.position["x"] += (self.velocity["x"]["mag"] * self.velocity["x"]["dir"])
        self.position["y"] += (self.velocity["y"]["mag"] * self.velocity["y"]["dir"])

        if self.position["x"] <= 0 or self.position["x"] >= screen_y:
            self.velocity["x"]["dir"] *= -1

        if self.position["y"] <= 0 or self.position["y"] >= screen_y:
            self.velocity["y"]["dir"] *= -1

        self.rect = pygame.Rect(self.position["x"], self.position["y"], size["width"], size["height"])

    def display(self):
        pygame.draw.rect(screen, self.color, self.rect)


def main():
    pygame.init()
    fps = 30
    clock = pygame.time.Clock()
    balls = []

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN:
                (x, y) = event.pos
                position = {"x": x, "y": y}
                new_ball = Ball(size, position, velocity)
                balls.append(new_ball)

        for ball in balls:
            ball.update()

        screen.fill((0,0,0))

        for ball in balls:
            ball.display()

        pygame.display.update()
        clock.tick(fps)

if __name__ == "__main__":
    main()

回答1:

Your problem with the right of the screen is due to a typo in this line in update():

if self.position["x"] <= 0 or self.position["x"] >= screen_y:
                                                         # ^ should be x

This prevents your Balls from entering the rightmost 640 - 480 == 160 pixels of screen.

The balls all behave the same because you only call randint to get random values once, when you first create velocity. Try moving the randint call into __init__, e.g.

def __init__(self, size, position, velocity=None):
    if velocity is None:
        velocity = {"x": {"mag": random.randint(3,7), 
                          "dir": random.randrange(-1,2,2)}, 
                    "y": {"mag": random.randint(3,7), 
                          "dir": random.randrange(-1,2,2)}}
    self.size = size
    self.position = position
    self.velocity = velocity
    self.color = (255, 255, 255)

This allows you to provide a velocity or be assigned a random one. In main(), you can then call:

balls.append(Ball(size, position))

to add a new Ball at the mouse position with random velocity.

On a side-note, you could simplify your position and velocity attributes to (x, y) tuples, as used in pygame, rather than your dict structures, i.e.:

velocity == (velocity['x']['mag'] * velocity['x']['dir'],
             velocity['y']['mag'] * velocity['y']['dir'])

position == (position['x'], position['y'])

Then your call in main() could be:

balls.append(Ball(size, event.pos))


标签: python pygame