1
\$\begingroup\$

I am making an "game" that has pathfinding using pygame. I am using Astar algo.

I have a main loop which draws the whole map. In the loop I check for events. If user press "enter" or "space", random start and end are selected, then animation starts and it will try to get from start to end.

My draw function is stupid as hell right now, it works as expected but I feel that I am doing it wrong. It'll draw everything to the end of the animation. I am also detecting events in there as well. What is a better way of implementing the draw function such that it will draw one "step" at a time while checking for events?

empty ui with no route draw User can press "space" to randomly choose start/end coords, then the app will animate it. Or, user can choose the start/end by left-click/right-click.

after the user start iteration During the animation, the user can also left-click to generate blocks, or right-click to choose a new destiantion.

Where I am stuck at is how to handle the events while the app is animating. Right now, I am checking events in the main loop, then when the app is animating, I do event checks again. While it works fine, I feel that I am probably doing it wrong.

What is the proper way of setting up the main loop that will handle the events while the app is animating?

In main loop, the app start animating once user choose start/end. In my draw function, I am putting another event checker in there.

def clear(rows):
    for r in range(rows):
        for c in range(rows):
            if r%3 == 1 and c%3 == 1:
                color = brown;
                grid[r][c] = 1;
                buildCoor.append(r);
                buildCoor.append(c);
            else:
                color = white;
                grid[r][c] = 0;
            pick_image(screen, color, width*c, height*r);

    pygame.display.flip();
    os.system('cls');

# draw out the grid
def draw(start, end, grid, route_coord):
    # draw the end coords
    color = red;
    pick_image(screen, color, width*end[1],height*end[0]);
    pygame.display.flip();

    # then draw the rest of the route
    for i in range(len(route_coord)):
        # pausing because we want animation
        time.sleep(speed);

        # get the x/y coords
        x,y = route_coord[i];
        event_on = False;

        if grid[x][y] == 2:
            color = green;
        elif grid[x][y] == 3:
            color = blue;

            for event in pygame.event.get():
                if event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 3:
                        print "destination change detected, rerouting";
                        # get mouse position, px coords
                        pos = pygame.mouse.get_pos();

                        # get grid coord
                        c = pos[0] // width;
                        r = pos[1] // height;

                        grid[r][c] = 4;
                        end = [r, c];
                    elif event.button == 1:
                        print "user generated event";
                        pos = pygame.mouse.get_pos();

                        # get grid coord
                        c = pos[0] // width;
                        r = pos[1] // height;

                        # mark it as a block for now
                        grid[r][c] = 1;
                        event_on = True;

            if check_events([x,y]) or event_on: # there is an event
                # mark it as a block for now
                grid[y][x] = 1;

                pick_image(screen, event_x, width*y, height*x);
                pygame.display.flip();

                # then find a new route
                new_start = route_coord[i-1];
                marked_grid, route_coord = find_route(new_start, end, grid);

                draw(new_start, end, grid, route_coord);
                return; # just end draw here so it wont throw the "index out of range" error
        elif grid[x][y] == 4:
            color = red;
        pick_image(screen, color, width*y, height*x);
        pygame.display.flip();

    # clear route coord list, otherwise itll just add more unwanted coords
    route_coord_list[:] = [];

clear(rows);

# main loop
while not done:
    # check the events
    for event in pygame.event.get():
        # mouse events
        if event.type == pygame.MOUSEBUTTONDOWN:
            # get mouse position, px coords
            pos = pygame.mouse.get_pos();

            # get grid coord
            c = pos[0] // width;
            r = pos[1] // height;

            # find which button pressed, highlight grid accordingly
            if event.button == 1:
                # left click, start coords
                if grid[r][c] == 2:
                    grid[r][c] = 0;
                    color = white;
                elif grid[r][c] == 0 or grid[r][c] == 4:
                    grid[r][c] = 2;
                    start = [r,c];
                    color = green;
                else:
                    grid[r][c] = 1;
                    color = brown;
            elif event.button == 3:
                # right click, end coords
                if grid[r][c] == 4:
                    grid[r][c] = 0;
                    color = white;
                elif grid[r][c] == 0 or grid[r][c] == 2:
                    grid[r][c] = 4;
                    end = [r,c];
                    color = red;
                else:
                    grid[r][c] = 1;
                    color = brown;

            pick_image(screen, color, width*c, height*r);

        # keyboard events
        elif event.type == pygame.KEYDOWN:
            clear(rows);

            # one way to quit program
            if event.key == pygame.K_ESCAPE:
                print "program will now exit.";
                done = True;

            # space key for random start/end
            elif event.key == pygame.K_SPACE:
                # first clear the ui
                clear(rows);

                # now choose random start/end coords
                buildLoc = zip(buildCoor,buildCoor[1:])[::2];
                #print buildLoc;

                (start_x, start_y, end_x, end_y) = pick_point();
                while (start_x, start_y) in buildLoc or (end_x, end_y) in buildLoc:
                    (start_x, start_y, end_x, end_y) = pick_point();
                    clear(rows);

                print "chosen random start/end coords: ", (start_x, start_y, end_x, end_y);

                if (start_x, start_y) in buildLoc or (end_x, end_y) in buildLoc:
                    print "error";

                # draw the route
                marked_grid, route_coord = find_route([start_x,start_y],[end_x,end_y], grid);
                draw([start_x, start_y], [end_x, end_y], marked_grid, route_coord);

            # return key for user defined start/end
            elif event.key == pygame.K_RETURN:
                # first clear the ui
                clear(rows);

                # get the user defined start/end
                print "user defined start/end are: ", (start[0], start[1], end[0], end[1]);
                grid[start[0]][start[1]] = 1;
                grid[end[0]][end[1]] = 2;

                # draw the route
                marked_grid, route_coord = find_route(start, end, grid);
                draw(start, end, marked_grid, route_coord);

            # c to clear the screen
            elif event.key == pygame.K_c:
                print "clearing screen.";
                clear(rows);

            # go fullscreen
            elif event.key == pygame.K_f:
                if not full_sc:
                    pygame.display.set_mode([1366, 768], pygame.FULLSCREEN);
                    full_sc = True;
                    rows = 15;
                    clear(rows);
                else:
                    pygame.display.set_mode(size);
                    full_sc = False;

            # +/- key to change speed of animation
            elif event.key == pygame.K_LEFTBRACKET:
                if speed >= 0.1:
                    print SPEED_UP;
                    speed = speed_up(speed);
                    print speed;
                else:
                    print FASTEST;
                    print speed;
            elif event.key == pygame.K_RIGHTBRACKET:
                if speed < 1.0:
                    print SPEED_DOWN;
                    speed = slow_down(speed);
                    print speed;
                else:
                    print SLOWEST
                    print speed;

        # second method to quit program
        elif event.type == pygame.QUIT:
            print "program will now exit.";
            done = True;

    # limit to 20 fps
    clock.tick(20);

    # update the screen
    pygame.display.flip();
\$\endgroup\$
1
  • \$\begingroup\$ I may be wrong here, but I think the whole idea behind events is making asynchronous responses to the events. \$\endgroup\$ Oct 17, 2012 at 7:43

2 Answers 2

6
\$\begingroup\$

Just separate the events from the drawing. The normal method is to redraw all the time, not to wait for something to change. Normally your loop should be like this:

while loop:
    check events:
        # find routes, block path, whatever
    update things:
        # change the state of the game
    draw()

You shouldn't be thinking in terms of drawing one step at a time. If you have a thing that changes in steps, change that thing over a period of time, and while you're doing that, keep drawing whatever the current state of the thing is.

\$\endgroup\$
5
  • \$\begingroup\$ i shoulda made my code like this loop, silly me. thanks! \$\endgroup\$ Oct 17, 2012 at 19:47
  • \$\begingroup\$ hi, i have more question in my while loop, it checks for events first(whether or not user will start the animation). once the user selected the start and end coords, itll update the grid values, then draw the start and end points on the grid. now, while doing the animation, i still have to check if there is events(user can set up blocks, choose new end). so my question is, is it bad practice to put an event checker in the main loop, and MANY event checkers in other functions, such as the animation function? if that makes any sense!? \$\endgroup\$ Oct 18, 2012 at 2:48
  • \$\begingroup\$ Your animation function should not be a long-running loop of its own. Animation should be something that takes place over many iterations of the main loop, by changing the visual state of the object you're animating. This way, you keep just a single place checking events, a single place drawing the world, and so on. \$\endgroup\$
    – Kylotan
    Oct 18, 2012 at 12:27
  • \$\begingroup\$ Ive updated with my main loop and draw functions, please have a look if you can. \$\endgroup\$ Oct 18, 2012 at 14:24
  • \$\begingroup\$ You should only be calling flip once, at the end of your main loop. And you shouldn't be changing object values inside your draw call. Object values should change gradually over individual updates. \$\endgroup\$
    – Kylotan
    Oct 18, 2012 at 15:00
3
\$\begingroup\$

You need to redraw every time in your main loop (even if no event has occurred), don't wait for any event or change to occur. Don't make your draw functionality dependent on events.

In your main loop

  1. Check for events and make changes
  2. Draw (even if nothing is changed)
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .