Gerador Genérico de Jogos de RPG
Gerador Genérico de Jogos de RPG
INSTITUTO DE INFORMÁTICA
CURSO DE CIÊNCIA DA COMPUTAÇÃO
Porto Alegre
2014
UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL
Reitor: Prof. Carlos Alexandre Netto
Vice-Reitor: Prof. Rui Vicente Oppermann
Pró-Reitor de Graduação: Prof. Sérgio Roberto Kieling
Diretor do Instituto de Informática: Prof. Luís da Cunha Lamb
Coordenador do Curso de Ciência da Computação: Prof. Raul Fernando Weber
Bibliotecária-Chefe do Instituto de Informática: Beatriz Regina Bastos Haro
RESUMO
ABSTRACT
The RPG genre of games is a very important one. The game consists of an adventure in which
players interpret roles of heroes on their journeys and its set of rules are quite computable.
There is a long tradition of such games in various platforms, from board games to computer
systems. This study is aimed to develop a core to facilitate the creation of such games,
allowing through only external files to be given a shape and a story, ie, to allow quick editing
of game content making possible to create games easily. In this paper, besides the concrete
result of its implementation, are also described in two parts the design and the
implementation. The first discusses the decisions and goals, and second, analyzing the paths
followed in accordance with the program construction techniques and software engineering.
RESUMO.................................................................................................................4
LISTA DE FIGURAS................................................................................................6
LISTA DE TABELAS...............................................................................................7
1 INTRODUÇÃO E MOTIVAÇÃO..........................................................................11
2 ESTADO DA ARTE.............................................................................................15
3 O PROJETO.......................................................................................................20
3.2.1.1 Seta......................................................................................................................28
3.2.1.2 Painéis.................................................................................................................28
3.2.1.2.1 Painel de Leitura......................................................................................................................29
3.2.1.2.2 Menu........................................................................................................................................29
3.2.2 Personagem............................................................................................................................................29
4 IMPLEMENTAÇÃO.............................................................................................35
5 CONCLUSÃO.....................................................................................................58
6 BIBLIOGRAFIA..................................................................................................59
11
1 INTRODUÇÃO E MOTIVAÇÃO
O presente trabalho tem por objetivo implementar uma plataforma de jogos do tipo
RPG.1
O RPG é um jogo em que o jogador interpreta um personagem de ficção, seguindo o
enredo definido em um roteiro.
Como em qualquer jogo, há regras que o definem, porém o aspecto mais importante é
a existência de uma história, em que o jogo se desenvolve no seu desenrolar. No RPG,
existem dois tipos básicos de jogadores muito bem definidos: O primeiro tipo é o jogador
Personagem e o segundo é o Mestre do Jogo.
O jogo consiste em uma aventura em que os jogadores interpretam papéis de heróis
em suas jornadas, geralmente ambientadas em cenários que remetem a fantasias medievais,
com castelos, fortalezas, magos, princesas, monstros, etc. Ele é fortemente baseado na
fantasia dos jogadores e o papel do mestre do jogo, em inglês “Game Master”, é fundamental,
pois ele é quem define a história e controla o fluxo do jogo de acordo com os eventos
acontecidos, o que pode exigir muito de sua criatividade, pois embora a história possa ser
predefinida, os detalhes determinados pelas escolhas dos jogadores podem resultar em
situações imprevisíveis.
Exceto por esta flexibilidade no que diz respeito ao enredo, o jogo tem uma mecânica
bastante rígida e precisa, consolidada em GURPS,2 um acrônimo de “Generic and Universal
Role Playing System”, que se fundamenta em linhas gerais no resultado de uma função entre
dois elementos: as características dos personagens reduzidas a atributos numéricos, e a sorte,
jogada nos dados. O presente trabalho não segue estritamente essas regras, porque elas
envolvem aspectos que não são explorados em um RPG para computador, mas apenas um
modelo simplificado criado com base nelas.
Os atributos numéricos dos personagens geralmente são medidos através de
pontuações sobre características como força, inteligência, vitalidade, poder de ataque, defesa,
velocidade, etc.
O elemento de sorte é obtido pelos lances de dados, que nos sistemas tradicionais de
1 ROLE-PLAYING GAME. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2014.
Disponível em: <http://pt.wikipedia.org/w/index.php?title=Role-playing_game&oldid=39202841>. Acesso
em: 23 jun. 2014.
2 GURPS. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2014. Disponível em:
<http://pt.wikipedia.org/w/index.php?title=GURPS&oldid=38152668>. Acesso em: 23 jun. 2014.
12
RPG variam de acordo com a quantidade de lados: quatro, seis, oito, dez, doze, vinte.
Existe grande variedade de tipos de sistemas de RPG e cada um define a sua forma de
tratar com os eventos, mas em regra são dadas soluções que envolvem um sorteio cujo
resultado é informado por um ou mais lances de dados de acordo com as possibilidades dadas
pelos atributos do personagem. Para dar um exemplo, um combate em que, de acordo com o
nível dos envolvidos, é possível que para um personagem mais forte seja exigido menos sorte
do que para um personagem mais fraco. No caso poderíamos ter uma função de acordo com
os pontos de ataque.
Supondo então, para exemplificar, uma simples fórmula para ilustrar uma
circunstância normal do jogo, como a decisão sobre uma tentativa de atacar o inimigo ser ou
não bem-sucedida. Por exemplo, façamos com que seja necessário o jogador atingir pelo
menos vinte e cinco pontos, valor que arbitramos para o atributo pontos de defesa (PD) do
inimigo. Estes pontos são informados por uma soma dos pontos de ataque (PA) do jogador,
somados a um lance de um dado de vinte lados (D20).
A gama de possibilidades portanto ficaria definida de forma que, quanto mais pontos
de ataque o jogador tenha, maior a quantidade de resultados possíveis para ser bem-sucedido,
e portanto menos sorte seja necessária.
Vejamos algumas possibilidades de acordo com os Pontos de Ataque do Jogador. Para
o ataque ser bem-sucedido é necessário satisfazer a seguinte fórmula:
PA + D20 >= PD
Sendo PD=25, as possibilidades são:
PA >= 24: Nem é necessário o lance de dados, pois o menor resultado possível é 1.
PA = 23: Qualquer resultado maior que 1 é suficiente.
PA = 22: Qualquer resultado maior que 2 é suficiente.
E assim por diante até:
PA = 5: Apenas com 20 é bem-sucedido.
PA < 5: Impossível.
O demonstrado pelo exemplo é a regra simplificada de tratamento de eventos em
jogos de RPG cuja constante é fazer da soma da força do personagem mais sorte o evento
resultante.
Como se percebe, esta mecânica é bastante computacional, tornando o gênero muito
propício a criação de soluções bastante criativas.
No que diz respeito ao fluxo da história e suas improvisações, comuns na forma
original do jogo, não é o tema do presente trabalho analisá-las, pois consistem em um
13
de como quando alguém em sua casa decide ligar seu videogame e não fazer nenhuma outra
coisa.
Estes perfis de jogadores são multifacetados, pois muitas vezes podem conviver vários
na mesma pessoa. Assim, um RPG, que geralmente é um gênero muito absorvente e que
carrega consigo as vezes até a estigmatização pela quantidade de tempo que consome da vida
da pessoa, pode limpar-se dessa carga se jogado na forma casual, em que o progresso
incremental tem relação de proporção direta com o tempo inútil da pessoa e não de proporção
inversa com as coisas que deixou de fazer.
Além disso, teria outras vantagens que jogos casuais mais simples não proporcionam,
mas pelo contrário, pois as vezes criam frustrações baseadas na perpétua repetição do mesmo
desafio apenas com dificuldade incrementada, ou na sensação de tempo perdido em toda a vez
em que se joga uma partida de um jogo, que é essencialmente sempre igual, e não se quebra o
próprio recorde.
Assim, um RPG casual, jogado em um tempo em que, pela circunstância, não teria
uma aplicação melhor, pode ser um gênero bastante promissor e popular.
2 ESTADO DA ARTE
Jogos de RPG tem sido implementados desde muito tempo, sendo o primeiro datado
de 1975 em mainframes PDP-10 e Unix.4 O trabalho porém traça uma linha que pinça alguns
em uma linha de tempo, que não tem a preocupação de ser uma rigorosa linha de evolução
dos jogos do gênero, mas apenas assinalar alguns critérios de incrementos de aspectos de
jogabilidade e aproximação com o objetivo do projeto.
A linha inicia em 1986 com uma implementação para o padrão MSX. O nome do Jogo
é Dungeon Master.5 É um jogo com os elementos básicos da mecânica e sem nenhuma
história, exceto o objetivo de progredir na exploração de labirintos, buscando encontrar
chaves que permitem entrar em níveis mais profundos, até encontrar um anel branco, o que
faz com que o jogador ganhe o título de “Dungeon Master”. Após isso, nada muda e ele
continuar jogando normalmente como se nada tivesse acontecido, aumentando a sua
experiência, matando inimigos e enriquecendo com os itens valiosos e ouro que siga
encontrando pelos labirintos.
O jogo é uma das primeiras implementações de RPG, e de certa forma antecipa o RPG
de ação, pois os combates acontecem quando o personagem do jogador e os inimigos tentam
ocupar a mesma posição. O jogador utiliza as teclas direcionais para fazer o personagem se
locomover pelo cenário e a barra de espaços para abrir os menus para visualização de
informações sobre o personagem, como experiência, quantidade de ouro, pontos de ataque,
defesa e itens do seu inventário.
Embora não tenha o que se possa chamar de uma história, possui muitos elementos de
RPG. O jogo começa em uma cidade onde existe uma estalagem, onde o personagem pode ver
suas informações, recarregar seus pontos de vida e gravar seu progresso e uma loja em que
podem ser comprados alguns itens com o ouro adquirido.
Na cidade só existem fantasmas e morcegos, os inimigos mais fracos, tanto pelas ruas
quanto pelas salas, e a medida que progride para lugares mais profundos encontra inimigos
mais desafiadores, até dragões.
4 RPG ELETRÔNICO. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2013.
Disponível em: <http://pt.wikipedia.org/w/index.php?title=RPG_eletr%C3%B4nico&oldid=34912200>.
Acesso em: 22 jun. 2014.
5 Dungeon Master (1986, MSX, ASCII) | Generation MSX. Disponível em: <http://www.generation-
msx.nl/software/ascii/dungeon-master/787/>. Acesso em: 23 jun. 2014
16
Nas salas, além dos inimigos, existem os baús que podem conter ouro ou itens e
basicamente o jogo se resume a isso: matar inimigos e coletar o conteúdo das arcas.
Figura 2.2 – Tela do jogo Dungeon Master em uma sala
6 FINAL FANTASY IV. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2014.
Disponível em: <http://pt.wikipedia.org/w/index.php?title=Final_Fantasy_IV&oldid=39175216>. Acesso
em: 22 jun. 2014.
17
ao jogador para que ele explore os lugares, converse com os personagens não jogadores e
trave os combates, sendo as vezes tirado do controle durante eventos importantes da história.
Este jogo oferece uma interface rica e com muitos menus para permitir ao jogador um
bom controle do jogo sem exigir habilidade, pois um dos traços essenciais do RPG, ao qual
este jogo é muito fiel, é ser um jogo mental e não de ação.
A interface alterna entre modos de navegação pelo mapa, diálogos e combate.
Na navegação, o personagem utiliza as setas direcionais e circula pelos mapas indo
atrás de cumprir os objetivos. O mecanismo do jogo faz com que a cada passo haja um sorteio
que defina se há ou não inimigos a serem combatidos nesta posição. Nos diálogos, pode-se
acelerar a exibição do texto e os menus são simples seleções e confirmações das opções
tomadas.
Figura 2.3 – Tela do Jogo Final Fantasy IV na ação no mapa do mundo
Fonte: Final Fantasy IV Part #2 - Cecil Slays a Dragon and Rescues a Damsel in Distress
(Disponível em: < http://lparchive.org/Final-Fantasy-IV/Update%2002/ >. Acesso em: 23 jun. 2014.)
O terceiro jogo da lista é Diablo.7 Lançado em 1996, trata-se de um jogo em que toda
sua ação ocorre na mesma interface. A parte gráfica é muito bem trabalhada, em perspectiva
isométrica, mas a novidade apresentada é a consolidação do RPG de ação, onde não existe o
cadenciamento da ação através de escolhas em menus. O personagem do jogador apenas
aponta para o inimigo e o ataca, e este por sua vez também o ataca como que em tempo real,
sem nenhuma mediação e nenhuma escolha do jogador que faça o jogo esperar. Embora a
história seja um pouco pobre, a mecânica de jogo foi muito bem definida.
Figura 2.4 – Tela do Jogo Diablo I
Após isso, quarto e último da lista é World of Worcraft.8 Este jogo é muito bem
elaborado na parte gráfica e bem complexo no que diz respeito ao mecanismo do jogo. A
grande novidade que este jogo traz porém é ser do gênero MMO, do inglês “massive
multiplayer online”, onde uma multidão de jogadores compartilha um universo comum.
Figura 2.5 – Tela do jogo World of Warcraft
3 O PROJETO
Basicamente o núcleo do jogo é possibilitar ao jogador andar com seu personagem por
um cenário e desta forma desencadear os eventos possíveis estabelecidos no jogo e dirigidos
pelo roteiro, como circular pelos mapas, entrar em combate com os inimigos, ler as narrações
da história estabelecidas no roteiro, conversar com personagens não jogadores e resolver as
missões (“quests”) que lhe são designadas.
Da perspectiva da interface, isso acontece com o jogador controlando o personagem
através das setas direcionais, lendo as informações nos painéis de leitura e fazendo as escolhas
definidas nos menus através do mouse.
A partir destas definições, o projeto divide os componentes em dois núcleos que
podem ser assim analisados e nomeados: Núcleo do Jogo, e Núcleo da Interface.
Personagens
Núcleo do
jogo
Mapas Roteiro
Character
Boss
elemento muito importante. No caso, a classe do jogador tem como atributo uma lista de itens
e uma lista especial categorizada de acordo com a funcionalidade do item que afeta os
atributos do personagem chamada de equipamentos.
Figura 3.3 – Diagrama de relações entre personagens e itens
Player
Equip Item
Item Weapon
Armor
Shield
Artifact
Inventário
Equipamento
Fonte: Próprio Autor
mágico, podendo ser usado em batalha ou não. O Elmo também é uma proteção. A Poção
consome-se com o uso e serve para recarregar pontos de vida ou de magia, ou incrementar
algum atributo temporariamente. O Anel incrementa algum atributo, O Papiro consome-se
com o uso e fornece algum poder mágico, Escudo é mais um equipamento que aumenta o
atributo de defesa. A Arma aumenta o atributo de ataque e define a forma do dano.
O sistema de batalhas é gerenciado a partir do núcleo do jogo, ele tem como pré-
condições o personagem do jogador e um inimigo estarem em posições que resultem
verdadeiro o teste de proximidade. Satisfeita essa condição, ocorre uma sequência de
comandos em que o jogador ataca, e caso o inimigo esteja vivo, ele ataca o jogador. A pós-
condição para o fim do combate é pelo menos um dos envolvidos nele estar morto.
A rotina segue este pseudocódigo:
Combate() := {
enquanto(Inimigo.Vivo && Jogador.Vivo) {
AtaqueDoJogador(Inimigo);
AtaqueDoInimigo(Inimigo);
VerificaçãoDeFimDeCombate();
}
}
AtaqueDoJogador(Inimigo) := {
Se (LanceDeDado()+Jogador.Ataque>Inimigo.Defesa) {
Inimigo.DanoRecebido(Jogador.DanoCausado());
Se (Inimigo.Vivo=Falso) {
SorteioDeItem();
Jogador.XP+=Inimigo.Nivel;
Se (Jogador.XP>=Jogador.PróximoNível
Jogador.SobeDeNível();
}
}
}
AtaqueDoInimigo(Inimigo) := {
Se (LanceDeDado()+Inimigo.Ataque>Jogador.Defesa)
Jogador.DanoRecebido(Inimigo.DanoCausado());
}
VerificaçãoDeFimDeCombate() := {
Se (Jogador .Vivo=Falso)
FimDeJogo();
Se (Inimigo.Vivo=Falso)
FimDoCombate();
}
26
O sistema de mapas trata dos cenários do jogo. Nele existem estruturas de dados que
contêm as informações dos tipos de cenário que serão exibidos na tela e os pontos de
transição entre mapas. A principal é um vetor bidimensional onde ficam armazenados
números correspondentes aos desenhos em uma paleta, que formam o desenho do mapa.
Existem outras estruturas que armazenam as transições entre mapas, a primeira
armazena os destinos e a segunda as origens. Na primeira os componentes são as coordenadas
cartesianas do ponto onde, caso o personagem assuma essa posição, ele deixa o mapa
corrente, e o número do mapa para onde ele vai.
A estrutura de origens, por sua vez é isomórfica a dos destinos em termos de tipo de
informação, porém, nesta, a função das coordenadas armazenadas é para inicializar o
personagem no mapa recém-chegado de acordo com o mapa de onde ele vem.
Além disso, ele contém informações básicas, como nome do mapa e quantidade de
inimigos posicionados nele.
Do ponto de vista das funções, há as que indicam os pontos de transição e que indicam
locais onde não é possível o personagem atravessar.
O roteiro contém as informações que dizem respeito a história do jogo. Todas as falas
e estados das jornadas ficam armazenadas neste componente.
O sistema tem como centro uma classe que armazena jornadas, chamadas de “quests”,
que controla um conjunto de informações e um conjunto de ações encadeadas que o
personagem pratica e que levam a progressão do estado da história.
É possível haver muitas jornadas, e elas estarem organizadas em forma de árvore, o
que permite alguma alinearidade no desenrolar da história, como ilustra o diagrama abaixo:
27
Q0.1
Q1.0
Q0.2
Q2.0 Q1.1
Q0.3
Q1.2
Q2.1
Q0.3
Fonte: Próprio Autor
No caso, existiria uma “quest” principal “Q0” e duas “side-quests” “Q1” e “Q2” com
seus respectivos estados. Para cada estado existem informações distintas, que são falas de
personagens, narrações de história e posições de personagens.
As mudanças de estado são definidas sobre determinados eventos que alteram o estado
da história.
Os eventos podem ser de diversas espécies como conversar com um NPC, matar um
Boss, atingir determinado nível, ou entrar em um mapa.
Então, a classe “quest” armazena os seguintes dados em quatro listas:
– Transições de estados;
– Falas e narrações;
– Posições de NPC;
– Posições de Boss.
Para essas quatro listas também foram definidas as classes que armazenam essas
informações.
As transições armazenam o status da “quest” onde ela se aplica, o evento e a
identidade do ente que dispara a transição, no caso em que se aplique, por exemplo, de ser
uma transição disparada por uma conversa com um NPC ou eliminação de um Boss.
As falas e narrações contêm o texto que será exibido ao jogador de acordo com o
status da “quest”, e contem a identidade do NPC, o status da “quest” e o conteúdo da fala.
As posições de NPC e Boss são isomórficas, e contém a identidade do personagem o
28
A interface gráfica é a parte do programa responsável por fazer com que os resultados
do processamento do jogo sejam postos na tela. Tendo em vista o jogo ter muitos
componentes, há diversos elementos que precisam existir para oferecer uma boa qualidade de
experiência, dos elementos de interação, à arte do jogo.
Os elementos de interação são aqueles que fazem a parte mais semelhante ao ambiente
de um sistema operacional. Embora sempre pensemos mais na parte artística e de desenhos,
os elementos básicos são indispensáveis para que tenhamos uma experiência interativa rica,
pois do contrário não seria um jogo, mas uma animação apenas.
3.2.1.1 Seta
Haja vista o jogo ter elementos que precisam ser apontados e selecionados. Foi
necessário criar este elemento trivial de interfaces gráficas que basicamente desenha uma seta
na tela conforme a posição do mouse.
3.2.1.2 Painéis
Os painéis servem para colocar texto ou figuras na tela. Todos derivam de uma classe
“Panel” que contem os elementos básicos de dimensões, fontes dos caracteres e bordas.
29
O Painel de Leitura serve para exibir as narrações do jogo e as falas dos personagens,
funciona armazenando o texto em páginas e parágrafos. Estes últimos são entidades definidas
para dar a cadência da leitura do texto. A página exige que sempre o texto comece a ser
exibido com o painel em branco e o parágrafo apenas em uma nova linha.
O componente imprime texto em uma região delimitada da tela e faz o efeito de
exibição progressiva do conteúdo com um retângulo da mesma cor do fundo que cobre o texto
e é progressivamente diminuído, linha após linha. O controle ainda permite a leitura rápida
clicando com o mouse sobre ele, o que causa a remoção dos retângulos que cobrem o texto.
3.2.1.2.2 Menu
3.2.2 Personagem
segue os dados fornecidos pelo sistema de mapas (PosiçãoMapa) e outro dado pelo tamanho
real no mundo (PosiçãoMundo) conforme o tamanho dado pelo bloco, ambos em uma relação
de proporção direta:
PosiçãoMundo = PosiçãoMapa * TamanhoBloco
Para a ocorrência de uma movimentação, portanto, a pré-condição, o personagem estar
em uma PosiçãoMundo cujo resto da divisão inteira pelo TamanhoBloco seja zero.
A animação é processada com o incremento da PosiçãoMundo de acordo com a
velocidade do personagem e a pós-condição é igual a pré-condição, o personagem estar em
uma PosiçãoMundo cujo resto da divisão inteira pelo TamanhoBloco seja zero.
DesenhaCena() := {
for(i = 0; i < Mapa.Largura; i++) {
for(j = 0; j < Mapa.Altura; j++) {
x = i * TamanhoBloco
y = j * TamanhoBloco
Desenha((x,y),Bloco[Mapa[i][j]]
}
}
}
DesenhaCena() := {
se (Mapa.MaiorQueTela) {
PosX,Y = PivotX,Y – CentroX,Y * TamanhoBloco
se (PosX,Y < 0)
PosX,Y = 0;
se (PosX,Y > Mapa.Largura * TamanhoBloco – LarguraTela)
PosX,Y = PivotX,Y – CentroX,Y * TamanhoBloco
}
MapaX,Y = PosX,Y / TamanhoBloco;
para (i = MapaX; i < LarguraTela + MapaX; i++) {
para (j = MapaY; j < AlturaTela + MapaY; j++) {
x = i * TamanhoBloco
y = j * TamanhoBloco
Desenha((x,y)Bloco[Mapa[i][j]]
}
}
}
Jogo
Núcleo Interface
A operação do núcleo do jogo utiliza uma máquina de estados que faz com que o
programa alterne entre as rotinas. Os principais estados são a navegação do personagem pelos
cenários utilizando as teclas de direção, a leitura de painéis, e os menus de combate. Ela
também avalia todas as condições de alternância entre elementos de interface, executa as
rotinas pertinentes aos estados, e as verificações necessárias para efetuar as transições entre os
estados.
A máquina de estados essencial do jogo foi então concebida desta forma, um estado
em que o personagem transita pelo mapa, outro de leitura dos painéis e outro de combate:
32
Map
Talk Combat
Fonte: Próprio Autor
Q++
Fonte: Próprio Autor
Q M
Q++ X T
Fonte: Próprio Autor
C
M, Q
T, Q C, Q
M, Q++
T, Q++ C, Q++
Fonte: Próprio Autor
9 MENEZES, P. B.; Haeusler E. H. Teoria das Categorias para Ciência da Computação. 2. ed. Porto Alegre:
Bookman, 2008. p. 162.
34
4 IMPLEMENTAÇÃO
Nucleo 1
Interface 1
Interface 2
Nucleo 2
Nucleo 3
Fonte: Próprio Autor
O Núcleo do jogo foi a primeira parte implementada, que contêm a base, que são o
personagem jogador e os inimigos e seus combates. Esta primeira fase foi testada em modo
texto diretamente e depois em formulários.
Dice
Enemy
Player
Dice
Fonte: Próprio Autor
A outra função, que lida com o ataque em si, envolveu a criação de uma expressão
38
regular para lidar com os danos que os personagens podem causar em combate.
O dano é informado por duas parcelas, uma pelos dados e outra, por um incremento,
como por exemplo “1d6+2” que, no caso, é resultado do sorteio de um dado de seis lados
mais dois. Há ainda a possibilidade de o personagem causar mais de um dano, como por
exemplo, uma arma que além de seu dano normal, causa ainda um dano mágico. A expressão
tem essa forma:
[#]d#[+#](;[#]d#[+#])*
O formalismo utilizado acima assemelha-se a um de expressões regulares10 e segue as
seguintes regras abaixo:
“#”, número;
“[ ]”, elemento opcional;
“( )*”, elemento iterativo que não precisa ocorrer;
“( )+”, elemento iterativo que precisa ocorrer pelo menos uma vez;
“( , )”, opção entre dois valores.
A função faz um parser de uma string, pois o projeto teve como objetivo ter seu
conteúdo fornecido por arquivos externos conforme o formalismo simples acima definido e
após roda os sorteios:
A chamada de função recebe como parâmetro expressão e antes do laço principal
declara a variável “value” com o valor de retorno em 0.
10 MENEZES, P. B.; Haeusler E. H. Teoria das Categorias para Ciência da Computação. 2. ed. Porto Alegre:
Bookman, 2008. p. 51.
39
return value;
}
Character
Item
NAME
string
ORIGINS
(# # #)+
[TRANSITIONS
(# # #)+]
[LEVEL
#]
[ENEMIES
#]
[RENEWABLE
(0,1)+]
MAP
(# )+ i x j
NAME
Cave of Lisgow
ORIGIN
230
TRANSITION
220
LEVEL
1
ENEMIES
2
RENEWABLE
0
MAP
41
888888888
844444448
846444448
844444448
884444448
884444488
884499998
884498888
888888888
O código do parser é bastante simples e explora a alternância entre linhas que indicam
nomes dos campos e dados.
private enum FieldName { NONE, NAME, ORIGIN, TRANSITION, LEVEL, ENEMIES, RENEWABLE,
MAP }
while (!sr.EndOfStream)
{
string s = sr.ReadLine().Trim();
FieldName auxFieldName = GetFieldName(s);
if (auxFieldName!=FieldName.NONE)
currentFieldName = auxFieldName;
else
DealWithFields(currentFieldName, s);
}
A classe ainda contempla funções para gerenciar a transição entre mapas e posições
por onde o personagem não pode andar. No caso, por simplicidade não foram implementadas
maiores abstrações do que os números pré-definidos do tileset:
Figura 4.4 – Paleta de blocos de cenário
if (tiles[j][i] == 6)
return true;
else
return false;
}
Na classe “Character” foi criada função para verificar a situação de proximidade entre
personagens, que são gerenciadas pelo núcleo do jogo, como iniciar conversas com NPCs e
combates com inimigos. A função verifica a posição de dois objetos “Character” no mesmo
perímetro.
Character
Item
MapInfo
um jogo como Pac-Man, os mesmos quatro fantasmas são exibidos na tela em qualquer
circunstância. Entretanto, um inimigo que existe apenas em um dado momento da história e
depois é eliminado, causaria a ocorrência de trechos de códigos especiais para lidar com a esta
situação, trazendo diversos desvios condicionais no código, o que inviabilizaria a
possibilidade de se utilizar conteúdo externo, pois de acordo com o conteúdo, deveria existir
sempre sua contraparte diretamente codificada, além de um código muito confuso.
A partir deste ponto, foi tomada uma decisão de projeto de evitar esta situação. Para
isso, foi adotada uma abordagem “top-down”, tendo como ponto de partida os arquivos de
conteúdo.
Abstraindo tudo que já estava pronto, a solução foi especificar o programa em torno da
seguinte questão:
Como serão os arquivos de conteúdo?
Obviamente, a parte gráfica ficou inalterada, os mapas apenas receberam um
cabeçalho indicando as transições, embora a multiplicidade de mapas não fosse algo que
essencialmente envolvesse o roteiro. A questão maior foi como criar o roteiro, o andamento da
história evitando uma explosão de estados.
A máquina de estados original era a descrita na Figura 3.7:
1 – Andar pelo mapa;
2 – Exibir texto;
3 – Combate.
O roteiro porém necessitava ser pelo menos um script linear, como um livro em que
você começa a ler na primeira página e termina na última, porém mais do que isso, um bom
RPG divide a história em “quests” que combinam sequencialidade e paralelismo. Ou seja,
seria minimamente uma lista, se o jogo seguisse uma história totalmente linear, ou uma
árvore, com a história sendo mais complexa.
Na solução encontrada, o jogo precisava ser um produto de duas máquinas de estado:
A do núcleo do jogo e a do roteiro. Do ponto de vista da especificação, um produto categorial,
o que resultou na repetição da máquina básica em cada nodo do roteiro, como descrito na
figura 3.10.
Foi adicionada a classe “Quest” (CygnusRPGLibrary/Game/Quest.cs) para lidar com
as “quests”. A “quest” é um elemento central do roteiro pois são nelas que se baseiam a
história. De acordo com o projeto, foi implementada a “quest” tendo como elemento principal
o seu estado, dado por um inteiro, que indica sua progressão e um booliano que indica se está
completa ou não, além de listas para posicionamentos de NPCs, Bosses, falas, e o principal,
45
Map
Talk Combat
Start StartGame
StartMenu
Lore TalkNPC
surrounding NPC
LoadGame action
MapAction
actio
WorldMenu n
surrounding enemy enemy dead
CombatMenu Attack
Enemy alive
player dies
End
UseItemMenu UseItem
MapAction
EquipMenu Equip
DropMenu Drop
MapAction
Save
A segunda, é a que opera as ações relativas a cada estado, são bastante específicas e
estão descritas na tabela abaixo:
Tabela 4.3 – Mapeamento das ações executadas em cada estado
Estado Ação
Start
StartMenu
StartGame Inicializa jogador e primeiro mapa do jogo.
LoadGame Carrega atributos do jogador, mapa e estado das quests.
InitGame
Verifica atualização estado de quest por fala do narrador e
TalkNarrator
carrega o texto no painel de leitura.
CheckQuestMap Verifica atualização estado de quest por entrada em mapa.
CheckQuestCombat Verifica atualização estado de quest por combate.
Lore
Verifica atualização estado de quest por fala de NPC e carrega
TalkNPC
o texto no painel de leitura.
MapAction
MapTransition Atualiza Tile Engine.
CombatMenu
Attack Ataca inimigo
Magic Lança magia no inimigo
Shop
WorldMenu
EquipMenu
Equip, Equipa o jogador com o item selecionado no menu.
DropItemMenu
UseItemMenu
UseItem Utiliza o item selecionado do menu.
PickUpMenu
neste caso. Caso não haja uma fala ainda não exibida, fica selecionada a fala repetida mais
avançada.
O segundo é a descrição do parser para aquisição de dados externos. Do ponto de vista
operacional do programa, o núcleo carrega dados do jogo através de arquivos externos. São
estes arquivos os “bitmaps” onde estão armazenados os “sprites”, dos personagens e dos
cenários; arquivos de texto descrição de mapas; arquivos de texto com narrações do roteiro da
história e falas dos personagens, além de arquivos de salvamento que armazenam o estado do
jogo.
O arquivo de texto que armazena a “quest” contém as informações organizadas no
formato conforme uma gramática mais elaborada que a do arquivo de mapas. Em regra, ela
utiliza comentários de linha, ou seja, a regra de desconsiderar o resto da linha após o caractere
“#” e utiliza o caractere “:” para estabelecer o tipo de informação e espaços em branco para
separar os parâmetros e aspas para unidades de texto.
A especificação, no caso, é a seguinte.
O primeiro tipo de informação é dado pelo tipo “ID”, que é o número de identidade da
quest.
A segunda é “NAME”, uma string com o nome da quest.
A terceira é “LORE”, que contém as falas, e os parâmetros são, identidade do NPC,
Status da “quest” quando ela é dita e o conteúdo das falas, este seguindo uma regra especial
que separa as falas de acordo com o formato de paginação do elemento de interface painel de
leitura, que divide o texto em parágrafos e páginas respectivamente com os caracteres
especiais “@” e “$”.
As transições (“TRANSITION”) operam as mudanças de estados nas quests, e têm
como parâmetros os o estado em que a “quest” transiciona, o evento, de uma lista (“NPC”,
“MAP”, “BOSS”, “END”), que correspondem a conversar com um NPC, entrar em um mapa,
eliminar um “boss” e o que indica o fim da “quest” e, por fim, a identidade do NPC, “boss”
ou mapa.
E por último “NPCPOSITION”, “BOSSPOSITION” são bastante semelhantes, e têm
como parâmetros a identidade, o status da “quest” em que ele ocupa naquele estado da
“quest”, o mapa e as coordenadas cartesianas da posição.
Um exemplo de conteúdo do arquivo, pode ser:
ID: 1
NAME: "Slay Bad Weasel"
LORE: 0 0 "Hi hero! Bad Weasel is making everyone's life miserable, please slay him!"
52
Character
Item
Equip
Boss
Quest
Screenplay
MapInfo
Fonte: Próprio Autor
Após isso, temos as falas que serão exibidas pelo narrador e pelos personágens em
função da "quest":
# LORE: NPC Id, Status, Text "paragraph1" @" paragraph2" @ "paragraph3" $ "paragraph4" @
"paragraph5" # @ paragraph separator, $ page separator
LORE: -1 0 "Fala -1A"
53
# Transitions: current status, thing, thing Id # thing: NPC, BOSS, MAP, ITEM
TRANSITION: 0 NPC -1
TRANSITION: 1 NPC 2
TRANSITION: 2 NPC 1
TRANSITION: 3 BOSS 1
TRANSITION: 4 NPC 1
TRANSITION: 5 BOSS 4
TRANSITION: 6 NPC 1
TRANSITION: 7 END -1
Temos assim, que a história desta “quest” seguirá os seguintes passos: Primeiramente
o narrador dirá sua fala, após, para haver o progresso, será necessário conversar com o NPC 2,
depois com o NPC 1, matar o BOSS 1, falar de novo com o NPC 1, Matar o BOSS 4, Falar
com o NPC 1 e por fim, após isso, a “quest” estará completa.
Por fim, os valores de posições de NPCs e Bosses são bastante evidentes, indicando a
Identidade, o estado da “quest” e a localização, dada pelo número do mapa e as coordenadas
de posição.
NPCPOSITION: 3 4 2 4 1
NPCPOSITION: 3 6 3 4 1
# Bosses Id, Status, Map, Position X, Position Y
BOSSPOSITION: 1 3 3 6 5
BOSSPOSITION: 4 5 2 4 2
screenPosition = SetScreenPosition();
if (tileEngine.BigMapWidth)
IfOutOfCenterX();
else
screenPosition.X = character.worldPosition.X;
if (tileEngine.BigMapHeigth)
IfOutOfCenterY();
else
screenPosition.Y = character.worldPosition.Y;
tileEngine.pivot = new Vector2(character.worldPosition.X,character.worldPosition.Y);
Esta função leva em conta a condição de o personagem servir de pivô para o tile
engine no caso em que o mapa desenhado ocupa um espaço maior do que a tela, tratado com
as funções IfOutOfCenter():
57
Por último, a implementação do “Tile Engine” também foi bastante fiel ao projeto, a
função de exibição do mapa na tela, com o duplo laço que lê as informações do mapa e
projeta na tela a correspondente imagem da paleta, ficou assim:
int rb = RigthBound();
int bb = BottomBound();
for (int j = this.mapPosition.Y; j < bb; j++)
for (int i = this.mapPosition.X; i < rb; i++)
{
Vector2 blockPosition = new Vector2(i * GameCore.TILESIZE – this.position.X,
j * GameCore.TILESIZE - this.position.Y);
Rectangle source = new Rectangle(Column(mapInfo.tiles[j][i]),
Line(mapInfo.tiles[j][i]), GameCore.TILESIZE, GameCore.TILESIZE);
spriteBatch.Draw(tileSet, blockPosition, source, Color.White);
}
58
5 CONCLUSÃO
6 BIBLIOGRAFIA