Table Of Contents

Previous topic

6. Help

Next topic

8. Paddle

This Page

7. Stay in world: part 2

As mentioned before, the method I used to keep the circle within the bounds of the canvas is not perfect: the circle sometimes goes partly out of bounds.

If your code works perfectly, then I congratulate you! If not, please read on and we will see how to fix it. However, before we start, let me show you what code I have in my Python Editor so that you can compare with yours.

radius = 10
dx = dy = 5
fps = 20          # frames per second
tbf = 1000/fps   # time between frames in ms

def update():
    global x, y, _id
    x += dx
    y += dy
    clear_screen()
    stay_in_world()
    write_help()
    draw_circle(x, y, radius, 'red')
    if pause:
        return
    _id = set_timeout(update, tbf)

def write_help():
    ctx.font = "30px sans-serif"
    ctx.fillStyle = "lightgrey"
    ctx.fillText("S to start the animation", 50, 100)
    ctx.fillText("P to pause the animation", 50, 150)
    ctx.fillText("R to resume after a pause", 50, 200)
    ctx.fillText("Q to quit: click BEFORE editing!", 50, 250)

write_help()

def stay_in_world():
    global x, y,  dx, dy
    if x < 0 and dx < 0:
        dx = -dx
        x += dx
    elif x > canvas.width and dx > 0:
        dx = -dx
        x += dx
    if y < 0 and dy < 0:
        dy = -dy
        y += dy
    elif y > canvas.height and dy > 0:
        dy = -dy
        y += dy

7.1. Keeping things simple

Let’s try to figure out how it works one step at a time. You may find it useful to grab a piece of paper and a pencil or a pen and jot things down.

We’ll focus first on the horizontal motion (the x coordinate). Suppose the circle is moving towards the right edge of the screen, so that its x coordinate is increasing at each frame. Let’s suppose we have dx=5, and that the radius of the circle is equal to 10. Furthermore, let’s suppose the canvas width is 100 (to keep things simple) and that at a given frame the center of the circle is at x=84.

Do it!

Make sure you follow along with a piece of paper, writing and/or drawing things to help you figure things out. I could have included images to show you how it works, but this would not help you as much to learn how to figure things out on your own which is the ultimate goal.

The leading edge of the cirle is at x + radius which is 94.

In the next animation frame, the position of the center of the circle (and that of its leading edge) increase by dx=5. So, the leading edge is at 99 which is still less than the canvas width: the circle is still inside.

In the next animation frame, the leading edge would be at 99 + 5 = 104 which is beyond the canvas width. So, the circle has to bounce back. By how much? ... Each frame, we want it to move 5 pixels horizontally. To reach the edge of the canvas, it moved by 1 pixel ... and moved 4 pixel beyond that. So, it should bounce back by 4 pixel and be located at 96. However, remember that this is the position of the leading edge of the circle; the center of the circle would be at 96 - 10 = 86.

You may want to use different numbers to have another example to compare with.

Let’s try to capture this using variables. To make things simpler, let’s focus on the position of the leading edge which we will call right_edge. By definition, right_edge = x + radius; we could also define left_edge = x - radius.

When right_edge [104] becomes greater than the canvas width [100], we want to change its position so that it is right_edge - width [104-100 = 4] less than width [100-4=96]. That is to say, we want:

right_edge = width -  (right_edge - width)

which we can rewrite as:

right_edge = 2 * width  - right_edge

and we want this to take place:

if right_edge > width and dx > 0:
    right_edge = 2 * width  - right_edge

However, when drawing a circle, we are not so much interested in where its edge is located as much as where its center is located. We know that we have right_edge = x + radius and thus:

if (x + radius) > width and dx > 0:
    (x + radius) = 2 * width  - (x + radius)

This is the same as:

if x + radius > width and dx > 0:
    x + radius = 2 * width - x - radius

Subtracting radius on both side of the equal sign on the second line, we find:

if x + radius > width and dx > 0:
    x = 2 * width - x - 2 * radius

which we can write as:

if x + radius > width and dx > 0:
    x = 2 * (width - radius) - x

Try it!

If you have not done so already, make the change in your code and test it thoroughly, changing the value of radius, dx and fps so that you can see that the circle really never goes beyond the right edge of the canvas. Remember that the correct variable to use is canvas.width and not simply width like we have written. Also remember to change the direction of motion of the circle when it bounces!

7.2. Three more times

So, we know how to prevent the circle from going beyond the right edge. We still have to figure out how to do the same for the left edge, as well as the top and bottom of the canvas.

Your turn!

Figure out how to do that. Take your time to go through it step by step. Then, when you are done and convinced that it works, click on the hint below so that you can see my solution.

Hint

Are you sure you did not click by mistake? Is your code working? if so, click below to see my solution.

Hint

def stay_in_world():
    global x, y, dx, dy
    if x < radius and dx < 0:
        dx = -dx
        x = 2*radius - x
    elif x > canvas.width - radius and dx > 0:
        dx = -dx
        x = 2*(canvas.width - radius) - x
    if y < radius and dy < 0:
        dy = -dy
        y = 2*radius - y
    elif y > canvas.height - radius and dy > 0:
        dy = -dy
        y = 2*(canvas.height - radius) - y