lunes, 9 de junio de 2014

First Game with Graphics, part 2

On the last post we managed to draw an image to the screen, and now we are going to make a program that allows us to move that image.

First of all, since our program is going to become a little longer, we must give it a little more structure. It's time to divide the program into classes, and give each piece a specific job. This will make it much easier to modify parts of the program without affecting others. Let's start by drawing the same image to the screen, but this time with classes



import pygame

# The ball class just needs to know two things:
# The image it's going to use and the position
# it's going to start in.

class Ball:

    def __init__(self,imageFile,position_arg):
        self.image = pygame.image.load(imageFile)
        self.position = (position_arg)

# This class manages all assets for the program.
# Currently it just loads the ball, but it could load
# and modify all kinds of things in the future.

class AssetManager:

    def load_ball(self,imageFile,position):
        return Ball(imageFile,position)

# The game class is pretty much the basic
# program we've been using so far, but in
# the shape of a class.

class Game:

    def __init__(self,resolution,caption):

        self.screen = pygame.display.set_mode(resolution)
        pygame.display.set_caption(caption)
        self.clock = pygame.time.Clock()
        self.running = True
        self.gray = (220,220,220)

    def check_events(self):

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False

    def draw_screen(self,ball):
        self.screen.fill(self.gray)
        self.screen.blit(ball.image,ball.position)
        pygame.display.flip()

    def main_loop(self,framerate,assets):

        ball = assets.load_ball('ball.png',(350,250))

        while self.running:

            self.clock.tick(framerate)
            self.check_events()

            self.draw_screen(ball)

        pygame.quit()



assets = AssetManager()
game = Game((800,600),"Game")
game.main_loop(60,assets)

I posted this code in some forums to get some feedback, since I'm new to object oriented programming. One user pointed out that having an asset manager was too much, and that the loading could be done from the ball class itself. It may be so, but for the time being I believe it's more logical that the game class communicates with an asset manager when it requires assets, rather than getting them itself. I'm not saying I'm right, but it works and I like it.

Now, to actually move the ball we're going to process the player's input. One may think that checking for key press events is the way to go, but their is a better way to control movement and that is with key.get_pressed().

This function returns a tuple of the entire keyboard, each key having a true or false value. If you run key.get_pressed at every loop, you can know which key is pressed at any given time.

Let's look at it in action:

class InputManager:

    # A constant speed. It's easier to just change it
    # here than going to every line of movement and
    # replacing it one by one.

    SPEED = 3

    def process_input(self,ball):

        # Here, we assign the variable key with the
        # entire map of keys being pressed.

        key = pygame.key.get_pressed()

        # You can ask if individual keys are being pressed
        # like this:

        if key[pygame.K_RIGHT]:
            ball.move(self.SPEED,0)

        # K_RIGHT is a constant that represents
        # the position of the right arrow key in the
        # tuple. It's just easier to remember this way.

        if key[pygame.K_LEFT]:
            ball.move(-self.SPEED,0)

        if key[pygame.K_DOWN]:
            ball.move(0,self.SPEED)

        if key[pygame.K_UP]:
            ball.move(0,-self.SPEED)

ball.move() isn't defined in this example, but it's just a function that moves the ball. Let's see the full code for the program:

import pygame
# The ball class now needs new functions.
# We can now set the postion of the ball
# and change it in the X or Y axis.

class Ball:

    def __init__(self,imageFile,position_arg):
        self.image = pygame.image.load(imageFile)
        self.position = position_arg

    def set_position(self,position_arg):

        self.position = position_arg

    def move(self,move_x,move_y):

        position_x = self.position[0] + move_x
        position_y = self.position[1] + move_y

        self.set_position((position_x,position_y))

class AssetManager:

    def load_ball(self,imageFile,position):
        return Ball(imageFile,position)

# I brought the check_events function
# here, to the input manager, since it made
# more sense.

class InputManager:

    SPEED = 3

    def check_events(self):

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game.running = False

    def process_input(self,ball):

        key = pygame.key.get_pressed()

        if key[pygame.K_RIGHT]:
            ball.move(self.SPEED,0)

        if key[pygame.K_LEFT]:
            ball.move(-self.SPEED,0)

        if key[pygame.K_DOWN]:
            ball.move(0,self.SPEED)

        if key[pygame.K_UP]:
            ball.move(0,-self.SPEED)

class Game:

    def __init__(self,resolution,caption):

        self.screen = pygame.display.set_mode(resolution)
        pygame.display.set_caption(caption)
        self.clock = pygame.time.Clock()
        self.running = True
        self.gray = (220,220,220)

    def draw_screen(self,ball):
        self.screen.fill(self.gray)
        self.screen.blit(ball.image,ball.position)
        pygame.display.flip()

    def main_loop(self,framerate,assets):

        ball = assets.load_ball('ball.png',(350,250))

        while self.running:

            self.clock.tick(framerate)
            input_manager.check_events()

           # It's important no to forget to add the new functions
           # to the main game loop.

            input_manager.process_input(ball)

            self.draw_screen(ball)

        pygame.quit()



assets = AssetManager()
input_manager = InputManager()
game = Game((800,600),"Game")
game.main_loop(60,assets)

Now we should be able to move the ball with the arrow keys!

In the next post, we'll move it around using the mouse. See you then.

No hay comentarios:

Publicar un comentario