Aula 14: Sons e imagens Kaya Sumire Abe kaya.sumire@gmail.com Diego de Faria do Nascimento df.nascimento93@gmail.com 14/11/2012 Resumo As últimas aulas abordaram programas que utilizam GUI e como tratar entradas do usuário. Além disso, também foram apresentadas algumas maneiras de desenhar formas de diferentes cores na tela. Neste capítulo, será apresentado como mostrar desenhos e imagens (denominadas sprites) e tocar música nos jogos. 1 Tópicos abordados nesta aula: Arquivos de imagem e som Desenhando sprites A função pygame.image.load() O tipo de dado pygame.mixer.sound O módulo pygame.mixer.music Sprite é o nome para uma imagem bidimensional que é utilizada como parte dos grácos de uma tela. Observe as guras 1 e 2. Figura 1: Exemplos de sprites. Os sprites são desenhados em cima de um cenário. Observe que é possível dimensionar, rotacionar e reetir a imagem, de forma que pode parecer que dois personagens estão se olhando. Também é possível desenhar o mesmo sprite várias vezes na mesma janela. O cenário do jogo, neste caso, pode ser considerado um grande sprite. O próximo programas tratará da utilização de sprites e tocar sons usando o Pygame. 1
Figura 2: Exemplos de sprites desenhados com um cenário. 2 Arquivos de imagem e som Os sprites são armazenados em arquivos de imagem do seu computador. Existem vários tipos de formatos de imagem que podem ser utilizados no Pygame, como BMP, PNG, JPG (e JPEG) e GIF. Você pode baixar imagens da internet para utilizar em seus jogos ou confeccionar seus próprios grácos com programas apropriados para tal. Os formatos de som que o Pygame suporta são MID, WAV e MP3. Você também pode baixar sons para utilizar no seu jogo, desde que os arquivos estejam nos formatos citados anteriormente. Se você possuir um microfone ou equipamento (software/hardware) adequado de som, você também pode confeccionar os efeitos sonoros do seu jogo. 3 Código-fonte Este programa é o mesmo que o da aula anterior, que lidava com entradas do usuário através do teclado e do mouse. Entretanto, ao invés de os desenhos serem criados no programa (como os quadrados verdes e o quadrado branco), são utilizados sprites, para representar os personagens. Além disso, música de fundo e efeito sonoro, quando a personagem comer uma das comidas do cenário, são adicionadas ao programa. Para obter os arquivos de imagem e som deste jogo, acesse http://inventwithpython.com/ resources/. Código 1: Sprites e sons. 1 import pygame, sys, time, random 2 from pygame.locals import * 3 4 # set up pygame 5 pygame.init() 6 mainclock = pygame.time.clock() 7 8 # set up the window 9 WINDOWWIDTH = 400 10 WINDOWHEIGHT = 400 11 windowsurface = pygame.display.set_mode((windowwidth, WINDOWHEIGHT), 0, 32) 12 pygame.display.set_caption( Sprites and Sound ) 13 14 # set up the colors 2
15 BLACK = (0, 0, 0) 16 17 # set up the block data structure 18 player = pygame.rect(300, 100, 40, 40) 19 playerimage = pygame.image.load( player.png ) 20 playerstretchedimage = pygame.transform.scale(playerimage, (40, 40)) 21 foodimage = pygame.image.load( cherry.png ) 22 foods = [] 23 for i in range(20): 24 foods.append(pygame.rect(random.randint(0, WINDOWWIDTH - 20), random. randint(0, WINDOWHEIGHT - 20), 20, 20)) 25 26 foodcounter = 0 27 NEWFOOD = 40 28 29 # set up keyboard variables 30 moveleft = False 31 moveright = False 32 moveup = False 33 movedown = False 34 35 MOVESPEED = 6 36 37 # set up music 38 pickupsound = pygame.mixer.sound( pickup.wav ) 39 pygame.mixer.music.load( background.mid ) 40 pygame.mixer.music.play(-1, 0.0) 41 musicplaying = True 42 43 # run the game loop 44 while True: 45 # check for the QUIT event 46 for event in pygame.event.get(): 47 if event.type == QUIT: 48 pygame.quit() 49 sys.exit() 50 if event.type == KEYDOWN: 51 # change the keyboard variables 52 if event.key == K_LEFT or event.key == ord( a ): 53 moveright = False 54 moveleft = True 55 if event.key == K_RIGHT or event.key == ord( d ): 56 moveleft = False 57 moveright = True 58 if event.key == K_UP or event.key == ord( w ): 59 movedown = False 60 moveup = True 61 if event.key == K_DOWN or event.key == ord( s ): 62 moveup = False 63 movedown = True 64 if event.type == KEYUP: 65 if event.key == K_ESCAPE: 66 pygame.quit() 67 sys.exit() 68 if event.key == K_LEFT or event.key == ord( a ): 69 moveleft = False 70 if event.key == K_RIGHT or event.key == ord( d ): 71 moveright = False 72 if event.key == K_UP or event.key == ord( w ): 73 moveup = False 74 if event.key == K_DOWN or event.key == ord( s ): 75 movedown = False 3
76 if event.key == ord( x ): 77 player.top = random.randint(0, WINDOWHEIGHT - player.height) 78 player.left = random.randint(0, WINDOWWIDTH - player.width) 79 if event.key == ord( m ): 80 if musicplaying: 81 pygame.mixer.music.stop() 82 else: 83 pygame.mixer.music.play(-1, 0.0) 84 musicplaying = not musicplaying 85 86 if event.type == MOUSEBUTTONUP: 87 foods.append(pygame.rect(event.pos[0] - 10, event.pos[1] - 10, 20, 20)) 88 89 foodcounter += 1 90 if foodcounter >= NEWFOOD: 91 # add new food 92 foodcounter = 0 93 foods.append(pygame.rect(random.randint(0, WINDOWWIDTH - 20), random. randint(0, WINDOWHEIGHT - 20), 20, 20)) 94 95 # draw the black background onto the surface 96 windowsurface.fill(black) 97 98 # move the player 99 if movedown and player.bottom < WINDOWHEIGHT: 100 player.top += MOVESPEED 101 if moveup and player.top > 0: 102 player.top -= MOVESPEED 103 if moveleft and player.left > 0: 104 player.left -= MOVESPEED 105 if moveright and player.right < WINDOWWIDTH: 106 player.right += MOVESPEED 107 108 109 # draw the block onto the surface 110 windowsurface.blit(playerstretchedimage, player) 111 112 # check if the block has intersected with any food squares. 113 for food in foods[:]: 114 if player.colliderect(food): 115 foods.remove(food) 116 player = pygame.rect(player.left, player.top, player.width + 2, player.height + 2) 117 playerstretchedimage = pygame.transform.scale(playerimage, ( player.width, player.height)) 118 if musicplaying: 119 pickupsound.play() 120 121 # draw the food 122 for food in foods: 123 windowsurface.blit(foodimage, food) 124 125 # draw the window onto the screen 126 pygame.display.update() 127 mainclock.tick(40) A execução deste programa é algo parecido com a gura 3. 4
Figura 3: Modelo de execução utilizando sprites e sons no jogo. 4 Como o programa funciona Assim como nos programas anteriores, nesta aula serão abordados apenas os conceitos novos em relação ao que já foi visto sobre interface gráca. Grande parte do código 1 já foi vista nas últimas aulas. 4.1 Inicializando a janela e as estruturas de dados Primeiro, a janela é criada com o título de `Sprites and Sound', na linha 12. Em seguida, entre as linhas 18 e 21, as personagens do jogos são criados. A variável player armazenará um objeto Rect que mantém a trajetória da personagem principal. Esta variável não conterá a imagem do jogador, apenas o tamanho e local em que a personagem se encontra. No início do programa, as coordenadas de cima e à esquerda do retângulo serão em (300, 100) e o jogador iniciará com 40 pixels de altura e largura. A segunda variável, playerimage, representa o jogador (a imagem que ilustra o jogador). A função pygame.image.load() recebe como parâmetro uma string que é o nome da imagem (que deve estar na mesma pasta que o programa). O retorno desta função é um objeto Surface que possui a imagem do arquivos desenhada em sua superfície. Este objeto Surface é armazenado em playerimage. 4.2 A função pygame.transform.scale() Na linha 20, o módulo pygame.transform é utilizado. A função pygame.transform.scale() pode diminuir ou aumentar a dimensão de um sprite. O primeiro argumento é um objeto pygame.surface, com a imagem desenhada. O segundo argumento é uma tupla com os novos valores de altura e largura da imagem. A função pygame.transform.scale() retorna um objeto Surface com o novo tamanho estipulado. Esta imagem alterada será armazenada em playerstretchedimage. Na linha 21, a função pygame.image.load() é chamada novamente para criar um objeto Surface com a imagem da cereja. Tenha certeza que o nome do arquivo na pasta em que estiver salvo e o nome que você utilizar no programa sejam exatamente os mesmos, do contrário, ocorrerá um erro. 5
Os objetos Surface armazenados em playerimage e foodimage são os mesmos utilizados na janela. No jogo, estas imagens são copiadas e mostradas (através do método blit()), para gerar a tela para o usuário. É o mesmo procedimento de quando um objeto Surface é retornado pelo método render() para objetos Font (como no programa do Hello World gráco). Ao invés de mostrar o texto, é necessário utilizar o blit() para este objeto Surface (e chamar o método update() para o objeto Surface). 4.3 Adicionando música e sons Entre as linhas 38 e 41, os sons utilizados no programa são carregados. Há dois módulos de som no Pygame: o pygame.mixer é responsável por executar pequenos efeitos de som durante o jogo e o pygame.mixer.music é utilizado para a música de fundo do jogo. A função construtora pygame.mixer.sound() é chamada para criar um objeto pygame.mixer.sound (que será chamado de Sound, por conveniência). Este objeto possui um método play() que, quando chamado, executa o devido som. Na linha 39, a música de fundo é carregada através do método pygame.mixer.music.load() que, por sua vez, é executada com o método pygame.mixer.music.play(). O primeiro parâmetro deste segundo método se refere a quantas vezes o mesmo som deve ser repetido depois da primeira vez que for executado. Por exemplo, se o primeiro parâmetro for 5, então o Pygame executará o mesmo som por 6 vezes seguidas. Passando o parâmetro -1, a música de fundo executará enquanto o programa estiver funcionando. O segundo parâmetro de pygame.mixer.music.play() é referente a em quanto tempo, após o início da música, esta deverá começar. Passando 0.0, ela iniciará a partir do início do arquivo de música. Se for passado 2.5, por exemplo, a música iniciará a partir de dois segundos e meio do início do arquivo da música. Finalmente, há um valor booleano chamada musicplaying que dirá ao programa se ele está executando a música e os efeitos sonoros ou não. Esta opção é útil para que se possa desativar o som de um jogo, item muito conveniente em um jogo. 4.4 Ativando e desativando o som O bloco condicional iniciado na linha 79 cuidará do som ser ativado ou não, no programa. Primeiro, é vericada se o usuário pressionou a tecla `m', para então ativar/desativar o som do jogo. Se musicplaying for True, então o som está ativado e pygame.mixer.music.stop() é chamada. Se for False, então oo som já está desativado e a pygame.music.play() é chamada. Este controle é feito através da variável musicplaying (que, no início, é declarada como True): caso a tecla `m' for pressionada e as condições forem vericadas, a variável musicplaying recebe um valor oposto ao que ela possui através de musicplaying = not musicplaying. 4.5 Desenhando o jogador na janela A linha 110 chama o blit(), utilizado em programas anteriores, para desenhar a imagem redimensionada, que representa o jogador, armazenada na variável playerstretchedimage em player 1. Neste caso, os objetos playerstretcherimage e player são desenhados em windowsurface. O segundo parâmetro no método blit() é um objeto Rect que especica onde o sprite deve ser desenhado. O objeto Rect armazenado em player mantém a posição do jogador na janela. 4.6 Vericando se o jogador colidiu com as cerejas Entre as linhas 114 e 119, há o código que verica a colisão entre a personagem e as cerejas. Este trecho é similar ao programa que tratava de colisões e entradas do usuário, com algumas linhas de código a mais. O método play() é chamado para que o som associado às cerejas seja executado, mas ele será executado apenas se musicplaying for True. Quando o jogador come uma das cerejas, ele aumentará de tamanho, de dois pixels por cereja. Na linha 116, um novo objeto Rect é criado para armazenar a variável do jogador que terá as mesmas dimensões do objeto anterior mais dois pixels para cada dimensão. 1 Lembre-se que o método blit() desenha o conteúdo de um objeto Surface em outro objeto Surface. 6
O objeto Rect que representa a posição e tamanho do jogador está na variável player, mas a imagem que representa o jogador está em playerstretchedimage. Desta forma, é necessário criar uma nova imagem com a nova escala. Este processo é realizado criando uma nova imagem redimensionada, através de pygame.transform.scale(). Assegure-se de que a imagem passada por parâmetro seja o objeto Surface original, playerimage, e não playerstretchedimage. Redimensionar uma imagem, geralmente, a distorce um pouco. Se uma mesma imagem for sempre redimensionada, ela poderá car cada vez mais distorcida do que sempre redimensionar a imagem original. Este é o motivo de o argumento, para que a nova imagem ajustada do jogador seja gerada, ser playerimage. 4.7 Desenhando as cerejas na janela No programa anterior, a função pygame.draw.rect() era chamada para desenhar um novo quadrado verde para cada comida que era gerada na tela, armazenada na lista foods. Entretanto, neste programa, os desenhos são sprites, ao invés de meros retângulos/quadrados. Desta forma, o método blit() é chamado, já que são objetos Surface que representam as cerejas. Este método é chamado e o objeto Surface armazenado em foodimage é passado por parâmetro. A variável food (que contém cada uma dos objetos Rect em foods a cada iteração do loop for) é utilizada para indicar, ao método blit(), a posição das cerejas, da mesma forma que é feito com a personagem do jogador. 5 Exercícios complementares 1. Altere o código de acordo com as seguintes situações: Crie um condição de parada para que o personagem pare de crescer. Crie um segundo personagem no jogo que seja controlado pelas teclas W, A, S e D. O antigo deverá ser controlado apenas pelas setas do teclado. O programa deverá detectar caso ocorra uma colisão entre os dois personagens e não permitir a intersecção. 7