Você está na página 1de 27

Trabalho prático nº 3: Pygame

Licenciatura em Engenharia Informática


Multimédia e Computação Gráfica 2022/2023

Trabalho Prático nº 3

Pygame1

Realizado em: 05/01/2023


Elaborado em: 07/01/2023

Samuel Pereira Martins nº 2021154569


Raul Marian Aconstantinesei nº2021155404

Multimédia e Computação Gráfica 2022/2023 Pág. 1 de 27


Trabalho prático nº 3: Pygame

Índice
1. Introdução ............................................................................................................. 3
2. Métodos.................................................................................................................. 3
3. Resultados............................................................................................................ 18
3.1. Menu Inicial .................................................................................................. 18
3.2. Inicio do jogo ................................................................................................ 19
3.3. Inimigos a disparar ...................................................................................... 19
3.4. Jogador a disparar ....................................................................................... 20
3.5. Morte de um inimigo .................................................................................. 20
3.6. Jogador alvejado .......................................................................................... 21
3.7. Game Over ................................................................................................... 21
3.8. Token para passar de nivel ........................................................................ 22
3.9. Variedade de níveis ..................................................................................... 23
3.10. Luta com o boss......................................................................................... 23
3.10.1. Luta com o boss – Fase 1 ...................................................................... 24
3.10.2. Luta com o boss – Fase 2 ...................................................................... 24
3.11. Token que duplica o dano ........................................................................ 25
3.12. Vitoria ......................................................................................................... 25
4. Discussão.............................................................................................................. 26
5. Conclusão ............................................................................................................ 26
Referências............................................................................................................... 27

Multimédia e Computação Gráfica 2022/2023 Pág. 2 de 27


Trabalho prático nº 3: Pygame

1. Introdução
Este projeto tem como objetivo o desenvolvimento de um jogo utilizado a linguagem
de programação Python.
O jogo desenvolvido é um jogo de plataformas onde o jogador vai poder saltar, correr
e eliminar os vários inimigos que estão em locais fixos no mapa e o seu objetivo é chegar ao
final de cada mapa sem perder as vidas todas para que no final enfrente um Boss no ultimo
nível.
Foram realizadas animações para as entidades mais importantes do jogo, o jogador, os
inimigos e o boss final. O jogador tem animação para quando fica parado, corre ou salta, os
inimigos como vão ficar fixos num local apenas vão ter uma animação e o Boss vai ter duas
animações que vão ser o seu estado normal e a segunda fase. O Boss entra nesta segunda fase
quando a vida chega a metade e faz com que o tamanho do sprite seja maior e tenham uma
aparência um pouco diferente.
Todas as funcionalidades vão ser melhor explicadas nas secções abaixo.

2. Métodos
Para que seja possível o bom funcionamento do jogo, é necessário ter um ideia de
como funciona a programação orientada a objetos sendo que é necessário criar várias classes
com diferentes métodos para as várias entidades do jogo.
A classe que foi mais trabalhosa foi a do jogador. Esta classe é constituída por vários
métodos para que seja possível o movimento, o disparo e as colisões.
Por isso foram criados os seguintes métodos para a classe do jogador:

class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.animation_list = []
self.index = 0
self.update_timer = pygame.time.get_ticks()
for i in range(4):
img = pygame.image.load(f'Player/{i}.png')
img = pygame.transform.scale(
img, (int(img.get_width() * 3), int(img.get_height() *
3)))

self.animation_list.append(img)

self.image = self.animation_list[self.index]
self.rect = self.image.get_rect()
self.jumped = False
self.jumping = False
self.vel_y = 0
self.flip = False
self.rect.center = (200, 400)
self.width = self.image.get_width()

Multimédia e Computação Gráfica 2022/2023 Pág. 3 de 27


Trabalho prático nº 3: Pygame

self.height = self.image.get_height()
self.in_air = False

O parâmetro animation_list serve para guardar os sprites2 que vão ser utilizados para
fazer as animações do objeto da classe Player que vão ser percorridas de uma forma
sequencial através da variável index que é inicializada a zero com um intervalo de tempo que
vai ser calculado através de outra variável update_timer. De seguida é necessário iniciar o
objeto com a sua primeira animação que vai ser feita através de um ciclo for que vai adicionar
os sprites ao vetor de animações. Outras variáveis também necessárias são variáveis de
controlo para que seja possivel saber se o Player está a saltar ou pode saltar, a velocidade do
salto, o lado para onde o player está virado e a posição onde vai ser inicializado.
Depois de ser inicializado, o Player vai ter acesso ao seguintes métodos:

def idle(self):
self.animation_list = []
for i in range(4):
if self.in_air:
img = pygame.image.load(f'Player/j{i}.png')
else:
img = pygame.image.load(f'Player/{i}.png')
img = pygame.transform.scale(img, (int(img.get_width() * 3),
int(img.get_height() * 3)))
img = pygame.transform.flip(img, self.flip, False)
self.animation_list.append(img)

A função Idle vai dar ao player uma nova animação enquanto este está parado,
dependendo se o player está totalmente parado ou a saltar no mesmo local, os sprites da
animação vão ser diferentes.

def move(self, x):


self.animation_list = []
if self.colisao(x):
x=0
self.rect.move_ip(x, 0)
for i in range(4):
if jumping:
img = pygame.image.load(f'Player/j{i}.png')
else:
img = pygame.image.load(f'Player/r{i}.png')
img = pygame.transform.scale(img, (int(img.get_width() * 3),
int(img.get_height() * 3)))
img = pygame.transform.flip(img, self.flip, False)
self.animation_list.append(img)

Esta função vai fazer com que o player se possa mover e que seja possível ter
animações de corrida e de corrida enquanto salta.

Multimédia e Computação Gráfica 2022/2023 Pág. 4 de 27


Trabalho prático nº 3: Pygame

def animation(self):
COOLDOWN = 100
try:
self.image = self.animation_list[self.index]
if pygame.time.get_ticks() - self.update_timer > COOLDOWN:
self.update_timer = pygame.time.get_ticks()
self.index += 1
if self.index >= len(self.animation_list):
self.index = 0
except:
self.index = 0

A função animation é utilizada para fazer a animação dos sprites por ordem e com
intervalos de tempo fixos para que a animação seja bem feita.

def jump(self):
dy = 0
if jumping and self.in_air == False:
self.vel_y = -27
self.in_air = True

Esta função permite fazer o player saltar e ao mesmo tempo verificar se existe alguma
colisão com o chão, isto permite que seja possivel interagir com os mapas que foram criados.

def getFlip(self):
return self.flip

def setFlip(self,aflip):
self.flip = aflip

Estes dois métodos servem para devolver o valor da variável flip da classe Player e para
alterar o valor dessa mesma variável.

def getX(self):
return self.rect.centerx

def getY(self):
return self.rect.centery

Estes dois métodos servem para devolver a posição do Player no ecrã. Vão ser
utilizadas, por exemplo, pelos inimigos para calcularem se vão disparar para a esquerda ou
direita, dependendo da sua posição em relação á do Player.

Multimédia e Computação Gráfica 2022/2023 Pág. 5 de 27


Trabalho prático nº 3: Pygame

def colisao(self,x):
for tile in world.obstacle_list:
#check collision in the x direction
if tile[1].colliderect(self.rect.x + x, self.rect.y,
self.width, self.height):
return True
return False

Este método tem como objetivo calcular se o retângulo do player fez colisão com
algum bloco do mapa para que seja possivel a interação do player com o mapa.

def createBullet(self, name):


pygame.mixer.Channel(6).play(pygame.mixer.Sound('enemy_gun.wav'))
return PlayerBullet(name, self.rect.centerx+10,
self.rect.centery-5)

Este método tem como objetivo criar balas que se vão deslocar na tela de jogo até que
colidam com algum objeto e as remova.

def reset(self):
self.rect.center = (200, 400)

Este método, quando chamado, vai desenhar o jogado na posição onde este inicia o
jogo.

def draw(self):
display.blit(self.image, self.rect)

Finalmente, o método draw vai desenhar esta classe na tela do jogador.


Outra classe bastante importante para tornar o jogo mais difícil para o jogador é a
classe inimigo que contem os seguintes métodos:

class Enemy(pygame.sprite.Sprite):
def __init__(self,x,y):
super().__init__()
self.animation_list = []
self.alive = True
self.index = 0
self.flip = True
self.update_timer = pygame.time.get_ticks()
self.vel_y = 0;

for i in range(4):
img = pygame.image.load(f'enemy/i{i}.png')
img = pygame.transform.flip(img,self.flip, False)
img = pygame.transform.scale(img, (int(img.get_width() * 3),
int(img.get_height() * 3)))
self.animation_list.append(img)

self.image = self.animation_list[self.index]

Multimédia e Computação Gráfica 2022/2023 Pág. 6 de 27


Trabalho prático nº 3: Pygame

self.rect = self.image.get_rect()
self.rect.center = (x,y)
self.width = self.image.get_width()
self.height = self.image.get_height()

A objeto da class enemy vai ser inicializado da mesma forma do player.

def whereisplayer(self,playerx):
if self.rect.centerx < playerx:
self.flip = False
else :
self.flip = True

Este método tem como objetivo virar o inimigo para o lado onde se encontra o player.
Vai ser passado como parâmetro a posição do jogador para que tal seja feito.

def animation(self):
COOLDOWN = 200
try:
self.image = self.animation_list[self.index]
if pygame.time.get_ticks() - self.update_timer > COOLDOWN:
self.update_timer = pygame.time.get_ticks()
self.index += 1
if self.index >= len(self.animation_list):
self.index = 0
except:
self.index = 0

Esta função tal como no player vai fazer o controlo da transição entre sprites.
def createBullet_left(self, name):
pygame.mixer.Channel(4).play(pygame.mixer.Sound('enemy_gun.wav'))
return EnemyBullet_left(name, self.rect.centerx-10,
self.rect.centery-15)

def createBullet_right(self, name):


pygame.mixer.Channel(4).play(pygame.mixer.Sound('enemy_gun.wav'))
return EnemyBullet_right(name, self.rect.centerx-10,
self.rect.centery-15)

Estes dois métodos são responsáveis por criar balas que vão ser adicionadas ao vetor
de balas para a esquerda ou direita. O vetor para onde vai ser adicionada a bala depende da
posição do player e esta vai ser determinada através dos métodos getX da classe player.

def hit(self,bullet):
if self.rect.colliderect(bullet):
self.kill

Este método vai determinar quando o inimigo é atingido por uma bala do player, e se
for, vai eliminar o objeto inimigo em questão.

Multimédia e Computação Gráfica 2022/2023 Pág. 7 de 27


Trabalho prático nº 3: Pygame

def move(self,scroll):
self.rect.centerx += scroll

Este método consiste em movimentar o inimigo ao mesmo tempo do mapa para


acompanhar o player. Deste modo, é possivel fazer com que o inimigo mantenha a sua posição
no mapa apesar de não ser apresentado no ecrão.

def gravity(self):
dy = 0
self.vel_y += 2
if self.vel_y > 10:
self.vel_y = 10
dy += self.vel_y
for tile in world.obstacle_list:
if tile[1].colliderect(self.rect.x, self.rect.y + dy,
self.width, self.height):
if self.vel_y < 0:
dy = tile[1].bottom - self.rect.top
self.vel_y = 0
elif self.vel_y >= 0:
self.in_air = False
dy = tile[1].top - self.rect.bottom
self.vel_y = 0
self.rect.y += dy

Este método consiste em puxar o jogador para baixo simulando assim a gravidade do
personagem. Para isso é definida uma variável que vai auxiliar o cálculo da altura que o
personagem irá descer em relação á altura que esta. Em geral, apenas as primeiras 6 linhas
deste código já fornecem gravidade ao personagem mas, para que o jogador não caia para o
infinito, é necessário o resto do código que vai verificar se existe colisão do personagem com
algum dos blocos do mapa e alterar o valor da variável dy para o valor necessário. Caso se
verifique que houve impacto com algum bloco é de seguida verificada a velocidade y do
jogador o que vai permitir descobrir se o jogador estava a cair ou a saltar quando colidiu com
o bloco. Caso ele esteja a saltar a posição vertical da aresta superior do retângulo que define o
personagem será igualada á posição vertical da aresta inferior do bloco em que colidiu. Caso
contrário a posição vertical da aresta inferior do personagem será igualada á superior do bloco
em que colidiu. Por fim a posição do jogador é atualizada em relação ao dy.

def getX(self):
return self.rect.centerx

def getY(self):
return self.rect.centery

Tal como no player, foram criados estes métodos. Apesar de não terem sido
utilizados, durante uma fase inicial do desenvolvimento do jogo estes métodos foram utilizados
para determinar a trajetória que a bala do inimigo deveria ter até chegar á posição inicial do
jogador. Apesar de não ter sido implementada, mantivemos as funções caso fosse necessário.

def draw(self):
display.blit(self.image, self.rect)

Multimédia e Computação Gráfica 2022/2023 Pág. 8 de 27


Trabalho prático nº 3: Pygame

Este método é utilizado para desenhar o inimigo no ecrã do jogador.


Como para o disparo é necessário uma entidade diferente, foi necessário criar classes
para as balas, estas podem ser do jogado ou dos inimigos:

class EnemyBullet_right(pygame.sprite.Sprite):
def __init__(self, name, x, y):
super().__init__()
self.image = pygame.image.load(name)
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.width = self.image.get_width()
self.height = self.image.get_height()

Os objetos da classe EnemyBullet_right e EnemyBullet_left vão ser as balas que vão


ser disparadas pelos inimigos e que vão parar na colisão com o jogador ou com o terreno.
Estas duas classes vão ter os seguintes métodos:

def update(self,player,stats):
self.rect.centerx += 10
if self.rect.centerx == 800:
self.kill()
if self.rect.colliderect(player):
stats.updateHP()
self.kill()

for tile in world.obstacle_list:


if tile[1].colliderect(self.rect.x - 5, self.rect.y,
self.width, self.height):
self.kill()

Este método vai atualizar a posição da bala dependendo da direção para onde esta vai
ser disparada. Por fim vai ser verificado se existe, ou não, colisão entre o jogador ou o
terreno.

class PlayerBullet(pygame.sprite.Sprite):
def __init__(self,name,x,y):
super().__init__()
self.path = 5
self.image = pygame.image.load(name)
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.width = self.image.get_width()
self.height = self.image.get_height()

Esta classe é bastante vai ser responsável por criar as balas do jogador, vão ter um
parâmetro a mais do que a classe EnemyBullet_right que vai ser self.path que corresponde ao
caminho que a bala vai percorrer, se for positivo vai se deslocar para a direita, caso contrario
vai se mover para a esquerda.

Multimédia e Computação Gráfica 2022/2023 Pág. 9 de 27


Trabalho prático nº 3: Pygame

def setPath(self,condition):
if condition:
self.path = -10
else :
self.path = 10

Este método vai alterar o self.path para positivo ou negativo para dar a direção das
balas do jogador.

def update(self):
self.rect.centerx += self.path

for _enemy in all_enemy:


if self.rect.colliderect(_enemy):
if _enemy.getAlive() == True :

pygame.mixer.Channel(5).play(pygame.mixer.Sound('enemy_death.wav'))
self.kill()
_enemy.setAlive(False)
_enemy.kill()
for _boss in gameboss:
if self.rect.colliderect(_boss.rect.x, _boss.rect.y,
_boss.width, _boss.height):

pygame.mixer.Channel(1).play(pygame.mixer.Sound('hit.wav'))
_boss.damage()
self.kill()

for tile in world.obstacle_list:


if tile[1].colliderect(self.rect.x + self.path/2,
self.rect.y, self.width, self.height):
self.kill()

Este método, também utilizado nas classes EnemyBullet_right e EnemyBullet_left, vai


atualizar a posição da bala e detetar se foi feita alguma colisão com o jogador ou terreno.

O jogador também tem uma barra de vida que é também uma classe e os seus
métodos são os seguintes:

class Stats(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.index = 0
self.image = pygame.image.load(f'Stats/hp_0.png')
self.rect = self.image.get_rect()
self.rect.center = (60,15)

Multimédia e Computação Gráfica 2022/2023 Pág. 10 de 27


Trabalho prático nº 3: Pygame

Esta classe vai servir para criar uma barra de vida. É constituída pelos parâmetros
index que vai ser utilizado para alterar o sprite que vai corresponder á vida atual do player.
Esta classe é constituída pelos seguintes métodos:

def updateHP(self):
self.index += 1

if self.index < 6 and self.index >= 0:


self.image = pygame.image.load(f'Stats/hp_{self.index}.png')
self.rect = self.image.get_rect()
else:
self.image = pygame.image.load(f'Stats/hp_6.png')
self.rect = self.image.get_rect()

Este método é responsável por atualizar o sprite de correspondente á vida do player a


cada vez que é atingido por uma bala inimiga ou pelo boss do jogo.

def gethp(self):
return self.index

Este método vai devolver a vida do player que corresponde ao index, se este tiver o
valor de seis então o jogador não tem mais vidas e vai recomeçar o jogo.

def reset(self):
self.image = pygame.image.load(f'Stats/hp_0.png')
self.rect = self.image.get_rect()
self.index = 0

Este método vai fazer com que o jogar tenha todas as vidas de novo. Este vai ser
chamado cada vez que o player passe de nível sendo que vai necessitar mais do que uma vida
por nível.

def draw(self):
display.blit(self.image, self.rect)

O método draw vai desenhar o sprite correspondente á vida no ecrã do jogador.

Este método vai corresponder ao método


O inimigo mais difícil de derrotar do jogo vai ser obviamente o Boss final e a sua classe
é constituída pelos seguintes métodos:

class Boss(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.animation_list = []
self.index = 0
self.damageplayer = True
self.second_stage = False
self.flip = True
self.stop = False
self.hp = 20

Multimédia e Computação Gráfica 2022/2023 Pág. 11 de 27


Trabalho prático nº 3: Pygame

self.update_timer = pygame.time.get_ticks()
self.damage_ = 1

for i in range(4):
img = pygame.image.load(f'boss/{i}.png')
img = pygame.transform.flip(img, self.flip, False)
img = pygame.transform.scale(img, (int(img.get_width() * 2),
int(img.get_height() * 2)))
self.animation_list.append(img)

self.image = self.animation_list[self.index]
self.width = self.image.get_width()
self.height = self.image.get_height()
self.rect = self.image.get_rect()
self.rect.center = (x, y)

Os objetos da casse Boss são iniciados de uma forma semelhante á do player e inimigo
, mas, este vai ter parâmetros adicionais como second_stage que vai fazer o Boss do jogo
entrar na segunda fase, o parâmetro hp que vai corresponder á vida do Boss, damage_ que vai
corresponder ao valor que vai ser subtraído á vida do boss e finalmente stop que vai fazer o
Boss parar durante poucos segundos cada vez que toque no jogador para que seja possivel ao
jogador que se possa reposicionar.

def secondstage(self):
if self.second_stage == False:
self.second_stage = True
self.rect.y -= 100

self.animation_list = []
for i in range(4):
img = pygame.image.load(f'boss/e{i}.png')
img = pygame.transform.flip(img, self.flip, False)
img = pygame.transform.scale(
img, (int(img.get_width() * 3), int(img.get_height() *
3)))
self.animation_list.append(img)

Este método é responsável por alterar o estado do Boss. Quando este método é
chamado, o Boss vai entrar no segundo estado que vai alterar os sprites de animação e o
tamanho deles o que vai fazer com que o jogador tenha de ser mais cuidadoso durante a luta
com o Boss

def animation(self):
COOLDOWN = 100
try:
self.image = self.animation_list[self.index]

Multimédia e Computação Gráfica 2022/2023 Pág. 12 de 27


Trabalho prático nº 3: Pygame

if pygame.time.get_ticks() - self.update_timer > COOLDOWN:


self.update_timer = pygame.time.get_ticks()
self.index += 1
if self.index >= len(self.animation_list):
self.index = 0
except:
self.index = 0

Este método tal como nas outras classes onde existe, vai fazer a animação dos sprites
a um determinado intervalo de tempo, neste caso 100 milissegundos.

def chase(self, x):


if self.stop == False:
if x < self.rect.centerx:
self.rect.centerx -= 1

if x > self.rect.centerx:
self.rect.centerx += 1

Este método leva como parâmetros a posição X do jogador, depois, comparando com
a posição do Boss, este vai se movimentar para a esquerda ou direita até chegar á posição do
jogador.

def gethp(self):
return self.hp

Este método vai devolver o valor da variável hp do boss que vai ser importante para
depois o fazer entrar na segunda fase.

def setDamage(self):
self.damage_ = 2

Este método vai se chamado para aumentar o valor que vai ser subtraído á vida do
Boss, para que este método seja chamado o jogador vai ter de colidir com um objeto que vai
estar escondido no mapa final na luta contra o Boss.

def damage(self):
self.hp -= self.damage_

Este é o método que vai remover vida ao Boss dependendo do valor da variável
damage_ .

def hit(self,player,stats):
if self.rect.colliderect(player):
self.stop = True
if self.damageplayer == True:

pygame.mixer.Channel(2).play(pygame.mixer.Sound('boss.wav'))
self.damageplayer = False

Multimédia e Computação Gráfica 2022/2023 Pág. 13 de 27


Trabalho prático nº 3: Pygame

stats.updateHP()

Sempre que existir uma colisão com o jogador, este método vai se chamado e vai
chamado e vai alterar o valor das variáveis stop e damageplayer, estas que servem para
controlar o dano que o boss vai causar ao jogador e para parar o boss durante alguns
segundos para dar tempo ao jogador para se reposicionar.

def continuar(self):
self.stop = False
self.damageplayer = True

Passado alguns segundos depois do Boss atacar o jogador, as variáveis que foram
anteriormente alteradas, vão voltar ao seu estado normal para que o Boss se possa voltar a
mover e causar dano ao jogador.

def move(self,scroll):
self.rect.centerx += scroll

Este método vai fazer com que o boss acompanhe o movimento do mapa.

def bosskill(self):
if self.hp <= 0:
self.kill()
self.bosskillsound()
return True
return False

Quando o Boss for derrotado, este método vai ser chamado, que vai eliminar o objeto
da classe Boss, e retorna um boolean que vai desbloquear a tela de vitoria para o jogador.

Para fazer uma imagem de fundo chamativa foi necessário criar duas classes, uma para
carregar imagens de fundo com animações para a resolução especifica da janela de jogo e outra
apenas para inserir imagens de fundo.

class Background(pygame.sprite.Sprite):
def __init__(self,name,x,y):
super().__init__()
self.index = 0
self.update_timer = pygame.time.get_ticks()
img = pygame.image.load(name)
self.image = pygame.transform.scale(img, (int(img.get_width() *
1.85), int(img.get_height() * 1.85)))

self.rect = self.image.get_rect()
self.rect.center = (x,y)

Multimédia e Computação Gráfica 2022/2023 Pág. 14 de 27


Trabalho prático nº 3: Pygame

A classe Background vai ter a função de inicializar imagens e fundo para depois serem
feitas animações. Foi utilizado para fazer as animações do menu inicial do jogo. Os métodos
desta classe são os seguintes :

def animation(self,acooldown,move):
COOLDOWN = acooldown
if pygame.time.get_ticks() - self.update_timer > COOLDOWN:
self.update_timer = pygame.time.get_ticks()
self.rect.centerx += -move
if self.rect.centerx <= 0:
self.rect.centerx = 1060

Este método vai fazer uma animação da própria imagem, movendo-a para a esquerda
até chegar a um limite. Quando o limite é alcançado, a posição da imagem é reposta para que a
animação pareça continua.

class LoadImage(pygame.sprite.Sprite):
def __init__(self,name,y):
super().__init__()
self.image = pygame.image.load(name)
self.rect = self.image.get_rect()
self.rect.center = (400,y)

A classe LoadImage apenas vai carregar imagens que vão ser utilizadas em várias partes
de jogo, como por exemplo, a imagens de “Game Over” , “Victory” e de “Restart”.

Para que o jogador consiga passar de nível, foi criado uma classe Token que vai criar
objetos que vão ser colocados no final de cada nível. Quando o jogador colide com esses
Tokens, vai ser mostrado o nível seguinte. Existe uma variação dos Tokens que servem para
amplificar o dano que o jogador vai causar ao Boss, este Token vai ser introduzido no ultimo
nível.

class Token(pygame.sprite.Sprite):
def __init__(self,x,y,tipo):
super().__init__()
self.tipo = tipo
if self.tipo == 1:
name = "next.png"
else:
name = "upgrade.png"
self.image = pygame.image.load(name)
self.rect = self.image.get_rect()
self.rect.center = (x,y)
self.level = 0

Os objetos da classe da Token vão ser inicializados de duas formas diferentes,


dependendo do parametro tipo. Quando o tipo for igual a 1 vai ser carregado uma imagem
quando é outro valor, a imagem vai ser outra.

Multimédia e Computação Gráfica 2022/2023 Pág. 15 de 27


Trabalho prático nº 3: Pygame

def update(self,player):
if self.rect.colliderect(player):
if self.tipo == 1:
self.kill()
self.level += 1
return True
else:
if level >= 3:
for _boss in gameboss:
_boss.setDamage()
self.kill()
return False

O método update ter duas funções dependendo do tipo do objeto. Se o tipo for 1
então vai ser um Token de passagem de nível, se for de outro tipo, vai servir para aumentar o
dano que o jogador vai causar ao Boss.

def move(self,scroll):
self.rect.centerx += scroll

Este método vai fazer com que o Token acompanhe o movimento do mapa.

Por fim a ultima classe que foi criada foi a classe World que vai carregar através de
ficheiros .csv os mapas do jogo. Isto é possível porque é aplicada uma grelha na janela de jogo
e cada espaço dessa grelha vai ser preenchido com um sprite de uma determinada dimensão.
O ficheiro .csv é necessário para guardar os números que vão depois corresponder ao nome
de ficheiros .png. Os métodos utilizados nessa classe foram os seguintes :

def __init__(self):
self.obstacle_list = []

Este método inicia o array que vai armazenar todos os obstáculos presentes no mapa
para depois poder ser usado para verificar a colisão dos personagens com o mapa.

def process_data(self, data):


for y, row in enumerate(data):
for x, tile in enumerate(row):
if tile >= 0:
img = img_list[tile]
img_rect = img.get_rect()
img_rect.x = x * TAMANHO
img_rect.y = y * TAMANHO
tile_data = (img, img_rect)
if tile >= 0 and tile <= 3:
self.obstacle_list.append(tile_data)

Multimédia e Computação Gráfica 2022/2023 Pág. 16 de 27


Trabalho prático nº 3: Pygame

if tile == 4:
nextlevel = Token(x*TAMANHO, y*TAMANHO,1)
next_level.add(nextlevel)
elif tile == 5:
enemy = Enemy(x*TAMANHO-20, y*TAMANHO-20)
all_enemy.add(enemy)
elif tile == 6:
leboss = Boss(x*TAMANHO,y*TAMANHO)
gameboss.add(leboss)
elif tile == 7:
Damage_Boost = Token(x*TAMANHO, y*TAMANHO,2)
next_level.add(Damage_Boost)

Este é o método que vai processar cada número presente no ficheiro csv que
representa o mapa. Para cada número que encontrar em cada célula do ficheiro csv vai
adicionar o respetivo bloco ao mundo. Caso o número esteja entre 0 e 3 será adicionado ao
array dos obstáculos já que estes são os blocos que o personagem utiliza para andar. Caso seja
o número 4, será adicionado um “token” que vai permitir ao utilizador interagir com ele e
passar para o próximo nível. Para o caso em que o número é 5, é adicionado um inimigo na
posição atual sendo que o 6 adiciona o boss. O numero 7 cria um “token” que permite ao
personagem duplicar o seu dano, passa assim a dar 2 de dano ao boss.

def removemap(self):
self.obstacle_list = []

Este método vai remover todos os blocos sólidos inseridos na lista de obstáculos,
fazendo com que o mapa desapareça do ecrã do jogador.

def screen_scroll(self,scroll):
for tile in self.obstacle_list:
tile[1][0] += scroll

Este método vai fazer com que o mapa mova dependendo da posição do jogador para
que seja possível construir mapas maiores do que o tamanho da janela com que o jogo é
iniciado.

Multimédia e Computação Gráfica 2022/2023 Pág. 17 de 27


Trabalho prático nº 3: Pygame

3. Resultados
Nesta secção do relatório vão ser mostradas imagens do jogo, com uma breve
explicação acerca do que se está a passar, dos resultados expectados e obtidos.

3.1. Menu Inicial


O primeiro contacto que o utilizador vai ter com a aplicação vai ser o menu de jogo:

Imagem 1 Menu Inicial

Esta será a interface que o utilizador irá observar ao entrar no jogo pela primeira vez.
Aqui, ao pressionar qualquer tecla o jogo irá começar como se pode observar na imagem
seguinte.

Multimédia e Computação Gráfica 2022/2023 Pág. 18 de 27


Trabalho prático nº 3: Pygame

3.2. Inicio do jogo

Imagem 2 Inicio do jogo

Aqui comeca então a aventura do jogador, 6 vidas e muitos inimigos pela frente
prontos para o impedirem de chegar ao seu lider.

3.3. Inimigos a disparar

Imagem 3 Inimigos a disparar

Como referido os inimigos vão fazer de tudo para impedir o jogador de chegar ao fim
inclusive disparar balas a partir do seu peito de cyborg. Podemos então observar na imagem
acima os inimigos a disparar apenas quando o jogador se aproxima deles.

Multimédia e Computação Gráfica 2022/2023 Pág. 19 de 27


Trabalho prático nº 3: Pygame

3.4. Jogador a disparar

Imagem 4 Jogador a disparar

O jogador pode disparar através do seu mouse. Ao clicar no clique esquerdo do


mouse ira ser lancada uma bala que permite matar os inimigos.

3.5. Morte de um inimigo

Imagem 5 Inimigo morto apos ser alvejado

Quando o jogador dispara contra os inimigos ele morrem apenas com 1 tiro como se
pode observar na imagem acima.

Multimédia e Computação Gráfica 2022/2023 Pág. 20 de 27


Trabalho prático nº 3: Pygame

3.6. Jogador alvejado

Imagem 6 Jogador foi alvejado

Quando o jogador é alvejado ele perde uma vida por cada tiro que sofre, na imagem
acima podemos observar que o jogador já foi alvejado 2 vezes. Caso o jogador perca as 6 vidas
ele irá morrer e será mostrada a tela persente na imagem abaixo mostrada.
3.7. Game Over

Imagem 7 Tela do Game Over

Multimédia e Computação Gráfica 2022/2023 Pág. 21 de 27


Trabalho prático nº 3: Pygame

Ao aparecer esta tela o utilizador terá de pressionar a tecla “R” para voltar a jogar. Ira
voltar para o menu inicial (Imagem 1) onde pode voltar a jogar. Isto implica ele começar o jogo
novamente no nível 1 e com a vida cheia.

3.8. Token para passar de nivel

Imagem 8 Token que permite passar de nível

Ao finalizar o nível matando, ou não, os inimigos, o jogador pode passar ao próximo


nível ao apanhar o Token que se encontra na imagem acima representada.

Multimédia e Computação Gráfica 2022/2023 Pág. 22 de 27


Trabalho prático nº 3: Pygame

3.9. Variedade de níveis


Foram criados 4 níveis em que 1 deles é a luta final com o boss. Pode-se observar nas
imagens abaixo alguns dos locais mais carregados dos 4 níveis disponíveis

Imagem 9 Local carregado nível 1 Imagem 10 Local carregado nível 2

Imagem 11 Local carregado nível 3 Imagem 12 Local carregado nível final

3.10. Luta com o boss


Apos chegar ao ultimo nível terá que lutar com o boss do jogo que precisa de 20 tiros
para morrer. O boss tem 2 fases o que torna a tarefa de o derrotar um bocado mais
complicada.

Multimédia e Computação Gráfica 2022/2023 Pág. 23 de 27


Trabalho prático nº 3: Pygame

3.10.1. Luta com o boss – Fase 1


No inicio o tamanho do boss é pequeno e ele quase não consegue atingir o jogador ao
ficar na plataforma do meio, ele sofre dano em qualquer lugar que o jogador acerte e precisa
de 10 tiros para passar para a segunda fase.

Imagem 13 Luta com o boss na primeira fase

3.10.2. Luta com o boss – Fase 2


Ao sofrer 10 de dano o boss passa então para a segunda fase onde o tamanho dele
aumenta e passa a sofre dano apenas nos olhos que ficam iluminados ao estar na segunda fase.

Imagem 14 Luta com o boss na segunda fase

Multimédia e Computação Gráfica 2022/2023 Pág. 24 de 27


Trabalho prático nº 3: Pygame

3.11. Token que duplica o dano


Para facilitar a tarefa de matar o boss foi adicionado um token que esta escondido na
gruta de onde o boss nasce que duplica o dano que o Boss sofre ao ser atingido pelo jogador.

Imagem 15 Token escondido na gruta do Boss

3.12. Vitoria
Ao derrotar o boss surgira o overlay da vitoria informando o jogador de que ganhou o
jogo. Neste overlay o jogador poderá carregar R para voltar ao menu inicial.

Imagem 16 Vitoria apos matar o boss

Multimédia e Computação Gráfica 2022/2023 Pág. 25 de 27


Trabalho prático nº 3: Pygame

4. Discussão
Na resolução deste projeto foram utilizadas cerca de 6 horas semanais o que
corresponde a cerca de 18 horas no total.
Foi conseguido fazer o pretendido no jogo, os fundamentais do jogo funcionam
bastante bem que são os movimentos do jogado e do Boss, as colisões, o disparo de balas
tanto do jogador como dos inimigos e finalmente as animações que foi uma parte bastante
divertida de fazer.
As forças do trabalho estão nas colisões entre o jogador e os obstáculos, que é
essencial para o bom funcionamento do jogo e as animações para dar ao utilizador satisfação
enquanto joga. Estes dois tópicos foram bastante trabalhados. Para isso assistimos alguns
vídeos de um canal que encontramos no Youtube que nos foi muito útil.3
Achamos este trabalho muito interessante de fazer já que nos mostra uma das
maneiras de criar um jogo em 2D. Existem maneiras mais fáceis de o fazer já que raramente se
encontram jogos criados em Pygame atualmente.
Tivemos algumas dificuldades no inicio do desenvolvimento do jogo já que ainda não
estávamos muito bem decididos com o jogo que iriamos fazer, isto fez com que perdêssemos
um pouco de tempo a criar muitos métodos que acabaram por nem ser usados no jogo final.
Por fim quando chegamos a um veredicto do jogo que iriamos desenvolver foi tudo
rapidamente desenvolvido por ambos.
Mais para o fim do desenvolvimento foi-nos sugerido pelo professor utilizar a
ferramenta do Git que permite sincronizar o ficheiro em ambos os computadores sem ser
necessário utilizar ferramentas externas tais como o Gmail, Discord, entre outras. Esta
ferramenta ajudou muito já que sempre que um de nos fazia uma alteração podia dar commit á
alteração e essa erá mostrada ao outro junto dos detalhes do que tinha sido alterado.

5. Conclusão
Pode-se então concluir que é possível desenvolver alguns jogos simples em Pygame
com um pouco de conhecimento sobre o sistema. Isto é, este projeto foi desenvolvido com o
conhecimento abordado em aulas que decorreram em 2 semanas ou menos o que totaliza
menos de 10 horas de aulas sobre o Pygame, logo claramente com um pouco mais de
conhecimento e tempo é possível desenvolver muitos jogos que são muito mais complexos
que o que foi desenvolvido por nós. Gostamos muito de programar em Pygame e das
funcionalidades que ele dispõem para a criação de jogos. Concluímos também que a utilização
de classes facilita muito a organização do código já que possibilita ter as funções de cada
objeto dentro dele.

Multimédia e Computação Gráfica 2022/2023 Pág. 26 de 27


Trabalho prático nº 3: Pygame

Referências
1. Pygame Front Page — pygame v2.1.4 documentation. https://www.pygame.org/docs/.

2. 2D Game Assets Store & Free. CraftPix.net https://craftpix.net/.

3. Coding With Russ.

Multimédia e Computação Gráfica 2022/2023 Pág. 27 de 27

Você também pode gostar