Processing math: 100%

Prijavi problem


Obeleži sve kategorije koje odgovaraju problemu

Još detalja - opišite nam problem


Uspešno ste prijavili problem!
Status problema i sve dodatne informacije možete pratiti klikom na link.
Nažalost nismo trenutno u mogućnosti da obradimo vaš zahtev.
Molimo vas da pokušate kasnije.

Moving multiple objects

So far, we have made animations in which various objects were moving (car, billiard ball, plane, text), but in each of these programs there was only one moving object. For the global variables that describe the scene we used the coordinates of that moving object, and sometimes we also used other values, such as displacement, direction of motion, and so on.

The movement of multiple objects is not fundamentally different from the movement of a single object - we will simply need to track values that describe movement for all of the objects. For example, we can represent each object with a tuple of values describing it, and keep all such tuples in a list.

Balls

In the following example we will see animated motion of several balls. Each ball is represented by a (x,y,dx,dy,r,color) tuple, where x,y are the coordinates of the center of the ball, dx,dy are the displacements of the ball per coordinate, r is the radius, and color is the color of the ball. All such tuples are placed in the list balls.

When unpacking a tuple from a list element (command x, y, dx, dy, r, color = balls[i]), as well as when returning it to the list (command balls[i] = (x, y, dx, dy, r, color)), we should mind the order of the variables.

In the example, we use the random module to create the balls in order to get random selections (imported using the import command). The random.randint(a, b) function returns a random integer between a and b (including boundaries), and the random.choice(a) function returns a random element from the collection a.

 
1
import random, pygame as pg, pygamebg
2
(width, height) = (500, 300)
3
canvas = pygamebg.open_window(width, height, "Balls")
4
5
colors = (
6
    pg.Color("red"), pg.Color("yellow"), pg.Color("blue"),
7
    pg.Color("cyan"), pg.Color("green"), pg.Color("purple")
8
)
9
10
# Make a list of 10 ball. The ball is determined by 
11
# position (x, y), displacement (dx, dy), size (r) and color.
12
balls = []
13
for _ in range(10):
14
    r = random.randint(10, 30)
15
    x = random.randint(r, width - r)
16
    y = random.randint(r, height - r)
17
    color = random.choice(colors)
18
    dx, dy = 0, 0
19
    while dx == 0 and dy == 0: # we don't want the balls that stand still
20
        dx = random.randint(-8, 8)
21
        dy = random.randint(-8, 8)
22
    balls.append((x, y, dx, dy, r, color))
23
    
24
def new_frame():
25
    global balls
26
    for i in range(10):
27
        x, y, dx, dy, r, color = balls[i]
28
        (x, y) = (x + dx, y + dy)
29
        if x - r < 0 or x + r > width: 
30
            dx = -dx
31
        if y - r < 0 or y + r > height: 
32
            dy = -dy
33
        balls[i] = (x, y, dx, dy, r, color)
34
        
35
    canvas.fill(pg.Color("darkgray"))
36
    for x, y, dx, dy, r, color in balls:
37
        pg.draw.circle(canvas, color, (x, y), r)
38
39
pygamebg.frame_loop(50, new_frame)
40

(PyGame__anim_balls_bouncing)

If we compare this program to the animation of motion of a billiard ball, we will notice a great similarity. The new_frame function is essentially different only in that now all actions (moving, bouncing, drawing) are done in a loop, since they need to be done for each ball. Setting the initial state is also somewhat more complicated, as there are more objects and we store multiple data for each of them, and we also use random choices.


In the previous example, all objects (balls) are present from the beginning to the end of the program. There are situations when we want some objects to cease to exist during program execution, or some new objects to appear (or both). In such cases, we can use an auxiliary list in the new_frame function in which we will put values that describe the new state. A typical sequence of activities is as follows:

  • create a new blank list at the beggining of the new_frame function

  • we go through the list of existing tuples, change them, and add the ones that we still need to the new list

  • if needed, supplement the list with new tuples

  • finally, we update the global list by putting in values from the new, auxiliary list

Let’s look at an example.

Stars

In this example, we simulate moving through space. The stars we encounter are represented with white circles and are determined by their position and radius.

For each frame, we compute a new auxiliary list that describes the next state. We move the stars following some rule, and we copy those that have not completely left the window to the next state list. After processing existing stars, we add new stars to the next state list so that the total number of stars would not decrease. Finally, we move all the stars to the global list so that we can calculate the next frame later.

40
 
1
import random, pygame as pg, pygamebg
2
(width, height) = (500, 300)
3
canvas = pygamebg.open_window(width, height, "Stars")
4
cx, cy = width // 2, height // 2
5
6
# A star is determined by its position (x, y) and size (r).
7
def new_star():
8
    r = random.randint(1, 3)
9
    x = random.randint(r, width - r)
10
    y = random.randint(r, height - r)
11
    return (x, y, r)
12
13
# Create a list of stars.
14
num_stars = 40
15
stars = []
16
for _ in range(num_stars):
17
    stars.append(new_star())
18
    
19
def new_frame():
20
    global stars
21
    next_stars = [] # list that will contain the next state
22
    for x, y, r in stars:
23
        x += 0.01 * (x-cx) # x moves away from the center of the window
24
        y += 0.01 * (y-cy) # y moves away from the center of the window
25
        r *= 1.01          # we see the star as bigger because we are "approaching"
26
        # if at least part of the star is in the window, we'll keep it
27
        if (x+r > 0 and x-r < width and y+r > 0 and y-r < height):
28
            next_stars.append((x, y, r))
29
            
30
    while len(next_stars) < num_stars:
31
        next_stars.append(new_star())
32
33
    stars = next_stars
34
    canvas.fill(pg.Color("black"))
35
    for x, y, r in stars:
36
        ix, iy, ir = int(x), int(y), int(r)
37
        pg.draw.circle(canvas, pg.Color('white'), (ix, iy), ir)
38
39
pygamebg.frame_loop(60, new_frame)
40

(PyGame__anim_trough_stars)

This example is given primarily to introduce a different way of working with a list of tuples that describe a scene. Because of that, some interesting details were left in the background. Let us look a bit at those, in this context, side details. To get the effect of approaching, the stars in front of us should move apart and magnify. Therefore, in the program, the values of x and y change so that the stars move from the center of the window to the edges, moving faster as they go further away from the center.

It’s not particularly important that you fully understand the commands that change the values of x, y, and r in order to learn programming, but you can try out how the animation changes when you slightly alter the coefficients in those commands (for example, stars can be moving slower, or growing faster).

Tasks

Snowflakes: Complete the program so that it works as shown in the example (“Play Task” button).

Each snowflake is described with only two numbers, its coordinates, so the tuples we will use will actually be pairs (x,y).

Snowflakes fall 1 pixel at a time, and those that go out of the window are replaced with new snowflakes. Forming a new state is similar to that of the “stars” program, only the rules for moving snowflakes are simpler.

The snowflakes in the initial set are selected anywhere in the window, and the ones that are added later are selected somewhere on the top edge of the window.

../_images/snowflake.png
8
 
1
import random, pygame as pg, pygamebg
2
(width, height) = (800, 400)
3
canvas = pygamebg.open_window(width, height, "Snowflakes")
4
5
snowflake_image = pg.image.load("snowflake.png")  # a snowflake image
6
snowflake_height = snowflake_image.get_height()
7
num_flakes = 10 # total number of the snowflakes
8

(PyGame__anim_snowflakes)

Outgoing balls: Copy the first program (balls) here, and modify it so that the balls do not bounce but continue to move away, and the ones that go off get replaced with new balls. This program is a combination of the two given examples (balls and stars), so try to use parts from both of these programs.

2

(PyGame__anim_balls_passing)

Gliding text Try out the program and try to understand how it works. Try altering something in the program (the text being displayed, the color in which the text is being displayed, the speed at which the text moves, or any other detail).

Challenge: Try to modify the program so that the text glides down instead of up.

56
 
1
import pygame as pg, pygamebg
2
3
(width, height) = (700, 250)
4
canvas = pygamebg.open_window(width, height, "Story")
5
text = (
6
    "Young children learn to walk",
7
    "by starting to walk.",
8
    "At first, they often fall,",
9
    "but they get up and keep going",
10
    "and with time",
11
    "they're getting better.",
12
    "Why not learn",
13
    "all the other skills",
14
    " that way?",
15
    " "
16
)
17
18
font = pg.font.SysFont("Arial", 40) # font for displaying the text
19
MARGIN_UP_DOWN = 30
20
TEXT_HEIGHT = 50
21
y_text_start = 200
22
i_first_visible_text_line = 0
23
num_visible_text_lines = 1
24
25
def draw():
26
    canvas.fill(pg.Color("skyblue")) # paint the background
27
    
28
    i_line = i_first_visible_text_line
29
    y = y_text_start
30
    for _ in range(num_visible_text_lines):
31
        # build an image of a text line and display it
32
        gray = min(230 - y, 192)
33
        color = (gray, gray, gray)
34
        text_image = font.render(text[i_line], True, color)
35
        canvas.blit(text_image, (20, y)) 
36
        i_line = (i_line + 1) % len(text)
37
        y += TEXT_HEIGHT
38
39
def new_frame():
40
    global i_first_visible_text_line, y_text_start, num_visible_text_lines
41
    y_text_start -= 1 # entire text glides 1 pixel up
42
    
43
    # check if the first line of text came out through the top
44
    if y_text_start < MARGIN_UP_DOWN:
45
        i_first_visible_text_line = (i_first_visible_text_line + 1) % len(text)
46
        y_text_start += TEXT_HEIGHT
47
    
48
    # is there room for another row
49
    next_line_pos = y_text_start + TEXT_HEIGHT * num_visible_text_lines
50
    if next_line_pos + TEXT_HEIGHT + MARGIN_UP_DOWN < height:
51
        num_visible_text_lines += 1
52
        
53
    draw()
54
55
pygamebg.frame_loop(30, new_frame)
56

(PyGame__anim_gliding_text)