quarta-feira, 10 de outubro de 2012

[GameMaker] Manual: criando seu primeiro RPG!

Fala galera, beleza?
Este guia está em construção e pode ainda haver possíveis pequenos erros e constantes atualizações.

Neste manual eu irei ensinar, passo-à-passo, como criar seu primeiro RPG utilizando o GameMaker. Lembre-se que um jogo é algo difícil de se fazer, e é necessário muito empenho e dedicação encima disso. Mesmo que você siga este guia à risca, seu jogo faltará muitas coisas importantes, como efeitos sonoros (links úteis à respeito disso no final do guia) e principalmente originalidade.

Por último, tenha em mente que este guia é realmente extenso, e o resultado ainda sim será bastante simples. Pode demorar até dias para você digerir todas as informações dele. Claro que você pode simplesmente sair copiando os códigos e fazendo o que eu digo. Depois você sequer vai saber utilizar a ferramenta em suas mãos, e a sua primeira alteração resultará num Erro que fechará o seu jogo. Eu não vou disponibilizar um Download do Tutorial pronto para evitar que isso aconteça. Se você vai só copiar tudo, aí já não é comigo. Não se esqueça que o real objetivo deste guia é ensinar como as coisas funcionam, assim você poderá começar a criar a partir do que aprender aqui. Pegue um tempo livre, abra seu Game Maker e venha comigo. Depois de acabado, pratique e personalize. 

Lembre-se que neste guia não usarei nenhum recurso exclusivo do Game Maker PRO.

Índice:
1. Preparativos
2. Criando os personagem
3. Obstáculos
4. Ataques e Habilidades 
5. Itens, equipamentos e PowerUps
6. Fazendo funcionar os atributos do personagem 
7. Configurando os Inimigos 
8. Npcs, negociantes e sistema básico de missões
9. Criando as rooms
10. Interface do jogo
11. Menu
12. Toques finais e Utilidades

1. Preparativos:

Primeiro de tudo, vamos organizar nossos recursos, seguindo algumas regras básicas:

1. Todos os Objetos terão o nome começando com "obj_" (como "obj_jogador", por exemplo), assim como todos os Sprites vão começar com "spr_", todas as Rooms como "rm_", todos os efeitos sonoros como "snd_" e todos os Scripts como "script_", nossos Backgrounds como "bg_" e todas as nossas variáveis vão começar com "var_". Nós não vamos utilizar Paths nem Time Lines, pois são recursos opcionais e facilmente substituídos.

2. Crie pastas para cada tipo de recurso na seção dos Objetos e dos Sprites, como, por exemplo, "jogador", "inimigos", "itens"... Futuramente você irá criar mais pastas conforme a necessidade.

3. Em todos os Sprites, aperte em "Center" para centralizar as coordenadas inciais das sprites, isso vai fazer diferença ao utilizar perseguição inimiga e ajudar a prevenir certos bugs de movimentação.

4. Crie um Sprite chamado spr_mask do mesmo tamanho padrão de todas as suas outras Sprites (8x8 nesse caso), mude a Mask dela para um padrão que você deverá estabelecer. Recomendo você usar o padrão "Full image + rectangle/disk". Não se esqueça de aplicar o passo acima neste sprite.
5. Crie o Objeto obj_persistent, e coloque ele como Parent TUDO que for de fato persistent. Não precisa ser diretamente. Isso vai ajudar a manter o controle nesses objetos, caso, por exemplo, você queira destruí-los numa room de Game Over sem precisar ser um à um.

Pode não parecer, mas estas simples regras farão uma IMENSA diferença no projeto, tanto na parte de organização como pode também evitar centenas de bugs futuramente (acontece quando um Objeto e uma Sprite possuem o mesmo nome, por exemplo).

Abaixo existe uma imagem com tudo pré-criado, desde Objetos até Sprites, pronto para ser trabalhado. Se quiser, tire um tempo agora para fazer o mesmo, isso vai agilizar o processo futuro.

Lembre-se: você pode usar as suas próprias Sprites ou não, porém eu irei ensinar utilizando como base Sprites do tamanho padrão de 8x8. Tente utilizar este mesmo tamanho, ou alguns códigos que utilizam coordenadas podem não funcionar corretamente.
2. Criando o personagem:

Conceito: 
Bom, como todo RPG possui diversos personagens, nós vamos ter que trabalhar muito nos personagens deste jogo. Nós vamos criar três personagens: um mago, um guerreiro e um arqueiro. Cada um terá três formas de ataque, mas isso será visto em breve.

Preparação:

Para fazermos tudo isso, temos que criar nossos recursos que serão utilizados. Devo avisar que muita coisa deve ser criada desde início, pois os personagens são de fato o objeto mais importante do jogo.

Crie as seguintes Sprites: spr_mago_0, spr_mago_90, spr_mago_180, spr_mago_270, spr_mago_morto, spr_arqueiro_0, spr_arqueiro_90, spr_arqueiro_180, spr_arqueiro_270, spr_arqueiro_morto, spr_guerreiro_0, spr_guerreiro_90, spr_guerreiro_180, spr_guerreiro_270, spr_guerreiro_morto.
Crie os seguintes Objetos: obj_personagem, obj_personagem_guerreiro, obj_personagem_arqueiro, obj_personagem_mago.
Crie os seguintes Scripts: script_jogador_setup_geral, script_jogador_setup_mago, script_jogador_setup_guerreiro, script_jogador_setup_arqueiro, script_jogador_movimento, script_jogador_sprite, script_jogador_arrumador.

AJUDANDO A ENTENDER: "O que significa o '0', '90', '180' e '270'??" - Bem, a visão do nosso personagem é medida em graus. Ou seja, 0 (graus) significa que ele está olhando para a direita, 90 (graus) significa que ele está olhando para cima, 180 (graus) para a esquerda, e 270 (graus) para baixo. Sim, você terá que desenhar todos os personagens olhando para as quatro direções.

Criando:

Agora que temos os nossos recursos necessários para este passo criados, vamos começar a trabalhar com alguns objetos e códigos. Esta parte é bastante simples e eu farei de tudo para explicar de uma forma que até o menos familiarizado com o Game Maker possa entender.

Vamos começar trocando algumas coisas nos objetos obj_personagem_guerreiro, obj_personagem_arqueiro e obj_personagem_mago. Clique em qualquer um dos três e siga as instruções abaixo:

- Coloque apenas o obj_personagem com Parent no obj_persistent (criado em 1. Preparativos).
- Coloque em todos os três seus Parent como obj_personagem.
- Coloque os três como Solid.
- Coloque os três como Persistent.
- Coloque os três com a Mask como spr_mask (criado em 1. Preparativos).

AJUDANDO A ENTENDER: "Qual a diferença de um objeto Solido e um não Sólido??" -  Inicialmente nenhuma... acontece que algumas funções do Game Maker tratam diferente objetos sólidos e não-sólidos, portanto marque sólido em tudo que seja um possível obstáculo, inclusive o próprio jogador, já que você não quer ver inimigos passando por você como se fosse um fantasma.
AJUDANDO A ENTENDER: "O que é Parent??" - Quando você marca um objeto como parente, todos os eventos do objeto apadrinhado que não foram alterados vão funcionar para o objeto afiliado, e o objeto afiliado também vai responder quando nos referirmos ao apadrinhado. Confuso? Deixe-me dar dois exemplos: 1) Supomos que no Create Event do obj_personagem tenha 2 scripts. Caso o obj_personagem_guerreiro, afiliado do obj_personagem, não tenha nada em seu Create Event ou tenha, mas tenha usado o "Call event", tudo o que está no Create Event do obj_personagem também acontecerá no obj_jogador_guerreiro. Isso evita fazer duas coisas iguais, e nos poupa de um imenso trabalho. 2) Agora vamos supor que o obj_inimigo queira atacar o personagem. Nós não vamos mandar ele atacar o obj_personagem_guerreiro, obj_personagem_mago e obj_personagem_arqueiro um por um. Imagina quanto trabalho fazer três coisas iguais desnecessáriamente. Ao invés disso, mandamos o inimigo atacar apenas o obj_personagem, pois os outros três, afiliados do obj_personagem, vão responder igualmente.
AJUDANDO A ENTENDER: "O que é Persistent??" - Quando o objeto é Persistent, basta colocar ele na primeira room, e ele será passado automáticamente para as próximas rooms mantendo todas as variáveis sem a necessidade de colocar ele novamente. Já quando a Room é Persistent, tudo o que acontece nela não é resetado, ou seja, ao retornar nela, tudo vai estar exatamante como você deixou antes de trocar de room.
AJUDANDO A ENTENDER: "O que é Mask??" - Mask é o sistema de colisão do Game Maker. Incialmente ela é exatamente igual ao Sprite. Supomos que o seu personagem seja um pequeno dragão e tenha uma calda irregular. Você não vai querer que esta calda atrapalhe o seu movimento, enroscando em obstáculos. Por isso, utilizamos uma Mask redonda, assim o personagem, mesmo não sendo redondo, vai ter uma forma redonda invisível. Mas por quê não usamos uma mask quadrada? Bem... você quem sabe, mas as pontas invisíveis do quadrado podem atrapalhar um pouco a sua movimentação.

Agora vá no script_jogador_setup_geral, e vamos criar as estatísticas dos nossos personagens desta maneira:

//Código de iniciação do personagem
{
    var_dano = 0;//Logo faremos o dano ser calculado de acordo com os atributos, estamos apenas criando a variável para usar mais tarde.
    var_energia = 1;
    var_energia_max = var_energia;//Limite máximo de energia, obs: colocado automáticamente, mas pode ser configurado manualmente
    var_mana = 1;
    var_mana_max =var_mana;//Limite máximo de mana, obs: colocado automáticamente, mas pode ser configurado manualmente
    var_municao =0;
    var_municao_max = var_municao;//Limite máximo de munição, obs: colocado automáticamente, mas pode ser configurado manualmente.
    var_especial =0;
    var_velocidade = 1 //Velocidade de movimento do personagem. Tome como base o tamanho dos sprites. Como usaremos 8x8, 1 de velocidade já é o suficiente.
    var_face =0 //A direção do personagem, ou seja, onde está a face dele.
    var_ataque_tempo =0;//Delay do ataque do personagem. Usaremos mais tarde.

    var_nivel = 1;
    var_experiencia =0;

    var_sprite_0 = 0;//Utilizaremos em cada personagem separadamente
    var_sprite_90 = 0;//Utilizaremos em cada personagem separadamente
    var_sprite_180 = 0;//Utilizaremos em cada personagem separadamente
    var_sprite_270 = 0;//Utilizaremos em cada personagem separadamente
    var_sprite_morto = 0;//Utilizaremos em cada personagem separadamente

    var_atributo_forca =0;
    var_atributo_inteligencia =0;
    var_atributo_vitalidade =0;
    var_atributo_destreza =0;

    var_equipamento_arma_nome ="Nenhuma"
    var_equipamento_arma =0; 
    var_equipamento_armadura_nome ="Nenhuma"
    var_equipamento_armadura =0;
}

Isso já é o suficiente. Como você pode ver, estamos preste à configurar a energia, mana, atributos e equipamentos dos nossos personagens. Isso é apenas uma base. Agora copie este mesmo código no script_jogador_setup_mago, script_jogador_setup_guerreiro, script_jogador_setup_arqueiro e faça as alterações necessárias para personalizar cada personagem, como por exemplo:

- No script_jogador_setup_arqueiro:
//Código de iniciação do personagem arqueiro
{
    var_energia = 10;
    var_energia_max = var_energia;
    var_mana = 10;
    var_mana_max =var_mana;
    var_municao =50;
    var_municao_max = var_municao;

    var_sprite_0 = spr_arqueiro_0;

    var_sprite_90 = spr_arqueiro_90;
    var_sprite_180 = spr_arqueiro_180;
    var_sprite_270 = spr_arqueiro_270;
    var_sprite_morto = spr_arqueiro_morto;

    var_atributo_forca =1;
    var_atributo_inteligencia =5;
    var_atributo_vitalidade =5;
    var_atributo_destreza =10;

    var_equipamento_arma_nome ="Arco comum"
    var_equipamento_arma =1; 
    var_equipamento_armadura_nome ="Armadura de couro"
    var_equipamento_armadura =1;
}

- No script_jogador_setup_guerreiro
//Código de iniciação do personagem guerreiro
{
    var_energia = 20;
    var_energia_max = var_energia;
    var_mana = 5;
    var_mana_max = var_mana;

    var_sprite_0 = spr_guerreiro_0;

    var_sprite_90 = spr_guerreiro_90;
    var_sprite_180 = spr_guerreiro_180;
    var_sprite_270 = spr_guerreiro_270;
    var_sprite_morto = spr_guerreiro_morto;

    var_atributo_forca =10;
    var_atributo_inteligencia =1;
    var_atributo_vitalidade =10;
    var_atributo_destreza =5;

    var_equipamento_arma_nome ="Espada comum"
    var_equipamento_arma =1; 
    var_equipamento_armadura_nome ="Armadura de ferro"
    var_equipamento_armadura =2;
}

- No script_jogador_setup_mago
//Código de iniciação do personagem mago
{
    var_energia = 10;
    var_energia_max = var_energia;
    var_mana = 20;
    var_mana_max =var_mana;
    var_municao =99;
    var_municao_max = var_municao;

    var_sprite_0 = spr_mago_0;

    var_sprite_90 = spr_mago_90;
    var_sprite_180 = spr_mago_180;
    var_sprite_270 = spr_mago_270;
    var_sprite_morto = spr_mago_morto;
    var_atributo_forca =5;
    var_atributo_inteligencia =15;
    var_atributo_vitalidade =5;
    var_atributo_destreza =1;

    var_equipamento_arma_nome ="Cajado comum"
    var_equipamento_arma =1; 
    var_equipamento_armadura_nome ="Armadura de couro"
    var_equipamento_armadura =1;
}

Lembre-se: Variáveis que não foram alteradas nos personagens não precisam ser repetidas. Note que a var_especial, var_velocidade, var_face, var_nivel, var_experiencia e var_ataque_tempo deverá permanecer igual, logo ela só está presente no script_personagem_setup_geral e já é o suficiente, e não precisa ser repetido.

Pronto. Scripts de criação prontos. Agora vamos colocar os scripts no Create Event de cada um dos personagens, sempre se lembrando de antes colocar um "Call Event". Deverá ficar parecido com isso:

AJUDANDO A ENTENDER: "O que é 'Call Event'?" - Esta função "chama" tudo que colocamos no mesmo evento do nosso obj no Parent. Por exemplo, colocamos um Script no Create Event do obj_personagem. O obj_personagem_guerreiro, tendo o obj_personagem como Parent, ao dar um 'Call Event' no Create Event o script que colocamos no obj_personagem se aplicará no Create Event dele também. Isso geralmente ocorre de forma automática, desde que não coloquemos nada nos Eventos do objeto afiliado. Concluindo: como colocamos o script_jogador_setup_guerreiro no Create Event do obj_personagem_guerreiro, logo temos que dar um 'Call Event' pois ele não vai mais ocorrer de forma automática.

Agora vamos trabalhar na movimentação e utilização correta das Sprites dos personagens. Vamos utilizar as teclas "A", "W", "S" e "D" para movimentar o personagem. Vá no script_personagem_movimento e vamos fazer um esquema básico de movimentação, utilizando o seguinte código:

//Movimento do personagem
{
    if keyboard_check(ord("W")) and place_free(x,y-var_velocidade) //Se a tecla "W" estiver sendo pressionada e nenhum obstáculo sólido se encontra na posição indicada, ou seja, encima do personagem...
        {
        y-= var_velocidade//Movimenta verticalmente para cima (pois está negativo) na velocidade do personagem
        var_face = 90//Como o personagem se movimentará para cima, a face dele ficará em 90º
        }
    if keyboard_check(ord("S")) and place_free(x,y+var_velocidade) //Se a tecla "S" estiver sendo pressionada e nenhum obstáculo sólido se encontra na posição indicada, ou seja, abaixo do personagem...
        {
        y+= var_velocidade//Movimenta verticalmente para baixo (pois está positivo) na velocidade do personagem
        var_face = 270//Como o personagem se movimentará para baixo, a face dele ficará em 270º
        }
    if keyboard_check(ord("A")) and place_free(x-var_velocidade,y) //Se a tecla "A" estiver sendo pressionada e nenhum obstáculo sólido se encontra na posição indicada, ou seja, à direita do personagem...
        {
        x-= var_velocidade//Movimenta verticalmente para direita (pois está negativo) na velocidade do personagem
        var_face = 180//Como o personagem se movimentará para a direita, a face dele ficará em 180º
        }
    if keyboard_check(ord("D")) and place_free(x+var_velocidade,y) //Se a tecla "D" estiver sendo pressionada e nenhum obstáculo sólido se encontra na posição indicada, ou seja, à esquerda do personagem...
        {
        x+= var_velocidade//Movimenta verticalmente para esquerda (pois está positivo) na velocidade do personagem
        var_face = 0//Como o personagem se movimentará para a esquerda, a face dele ficará em 0º
        }
}

Coloque este Script no Step Event do obj_personagem.

Agora vamos configurar o script_personagem_sprite para que ele troque a imagem do nosso personagem automáticamente conforme a necessidade, sem precisarmos trocarmos toda hora manualmente. Lembre-se que quando o personagem começar a atacar, nós vamos fazer uma pequena modificação neste Script, para que as Sprites deles atacando funcionem também. Por enquanto, o script_personagem_sprite deverá ficar assim:

//Configuração dos sprites do personagem. Por enquanto, nada precisa ser alterado.
{
var_andando =0;
switch(keyboard_key)//Checa se o personagem está andando ou não
    {
    case ord("A"):
    case ord("W"):
    case ord("S"):
    case ord("D"):
        var_andando =1; //Se alguma destas teclas (W,S,A ou D) foi pressionada, então ele está andando...
        break;
    }
   
    //Velocidade visual da sprite
    if var_ataque_tempo <=0 and var_andando=0//Se o personagem não está atacando e nem andando
        {
        image_speed =0; //Velocidade da sprite é zerado
        image_index =0; //Subimagem da sprite volta para a primeira e permanece nela
        }
    else
        {
        image_speed =0.2; //Velocidade da sprite vai para 0.2 (se for 1, vai ficar muito rápido)
        }

//=============================
//Configurar a sprite correta de acordo com a direção, ou seja, a face do personagem
switch(var_face)// Lembre-se que configuramos as sprites do personagem no script do setup de cada um, logo NADA precisa ser alterado por enquanto.
    {
    case 0: sprite_index=var_sprite_0; break;
    case 90: sprite_index=var_sprite_90; break;
    case 180: sprite_index=var_sprite_180; break;
    case 270: sprite_index=var_sprite_270; break;
    }
//=============================
}

AJUDANDO A ENTENDER: "O que seria um 'Switch'?" - Switch é um versão compacta e mais rápida de ser executada de vários 'If's'. Ao invés de você fazer if face =0 {}, if face =90 {} ... você resume tudo em um único switch, trocando os 'if's' pelo 'case' e não esquecendo de colocar um 'break' no final de cada resultado, ou não vai funcionar.


Agora coloque este Script junto com o script_jogador_movimento no Step Event do obj_personagem.

Agora crie uma Room chamada "rm_teste", coloque qualquer um dos três personagens (o obj_personagem NÃO é um personagem, ele é um representante que nunca vai ser colocado diretamente em nenhuma das rooms). Logo a seguir vamos criar muitos outros objetos como este), se quiser crie uma view (com cerca de 180x140 caso você esteja usando sprites 8x8) rápida apenas para focar melhor no personagem e veja se tudo está funcionando corretamente até agora.

Caso positivo, antes de iniciarmos os ataques e habilidades de nossos personagens, vamos fazer a primeira parte do script_jogador_arrumador. Este pequeno script cuidará automáticamente de variáveis para nós, como por exemplo, não deixar a var_energia ultrpassar a var_energia_max, que é o limite. Faça assim:

//Arrumador automático do personagem
{
    if var_energia > var_energia_max //Se nossa energia é maior que o máximo de energia permitido...
        var_energia = var_energia_max //A energia irá automáticamente se igualar ao máximo permitido, impedindo assim, a ultrapassagem.

    if var_mana > var_mana_max //Mesma coisa ao de cima, porém com a var_mana.
        var_mana = var_mana_max

    if var_especial >100//Esse "100" é um valor aleatório... é um bom número de golpe especial. 
    var_especial =100
}

Coloque-o no Step Event do obj_personagem, junto com os outros dois Scripts.

Logo mais vamos editar também este script para suprir futuras necessidades. Agora, guarde-o e vamos ao nosso próximo passo.

3. Obstáculos:

Conceito:

Antes de terminarmos os nossos personagens por completo, vamos terminar a movimentação dele. Pois para uma movimentação fazer sentido, temos que ter obstáculos, como pedras, paredes, grades, lagos. Nosso personagem não consegue passar por nenhum destes obtáculos... mas e uma flecha? A flecha não conseguirá passar por uma pedra, nem uma parede... mas por uma grade e um lago, obviamente ela consegue. É isso que vamos criar.

Preparação:

Crie os seguintes Sprites: spr_obstaculo_alto, spr_obstaculo_raso.
Crie os seguintes Objetos: obj_obstaculo_geral, obj_obstaculo_raso, obj_obstaculo_alto
Crie o seguinte Background: bg_obstaculos

Criando:

Com os nossos recursos que acabamos de criar, você deve estar no mínimo confuso. "Onde estão as árvores, os lagos, as paredes, as rochas?" você me pergunta. Bem, é importante saber que quanto menos objetos criarmos, mais leve será nosso jogo e conseqüentemente melhor desempenho teremos. Logo teremos apenas dois obstáculos: obj_obstaculo_raso (no qual permitirá a passagem de projéteis) e obj_obstaculo_alto (não permitirá a passagem de projéteis).

Você não precisa criar nenhum código. Apenas faça algumas configurações básicas:

- O Parent do obj_obstaculo_alto e obj_obstaculo_raso devem ser o obj_obstaculo_geral.
- As Sprites spr_obstaculo_raso e spr_obstculo_alto devem ser, de preferência, quadrados ocos (para não atrapalhar a sua visão) e de cores distintas, como vermelho e amarelo. Isso vai ajudar a diferenciá-los.
- Marque a opção Solid em ambos.
- Desmarque a opção Visible em ambos.
- Coloque a Mask de ambos como spr_mask como de costume.

AJUDANDO A ENTENDER: "O que 'Visible'?" - Visible, como o nome já implica, configura para o objeto estar vísivel ou invisível durante o jogo. Ambos os casos, ele ainda estará lá, existindo, e a sua colisão também. Isso é útil para criar armadilhas (com botões invisíveis no chão) e coisas do tipo.

Agora vamos à parte estética. No bg_obstaculos, crie uma folha de cerca de 64x64 e configure a Grid para se dividir em 8x8 (ou do tamanho padrão de suas Sprites).
Desenhe cada obstáculo em cada quadradinho separado. Se precisar, dê um zoom.
Você não precisa fazer tudo agora, desenhe apenas quatro para testes e dê OK. Na tela de configuração do background, selecione a opção "Use as Tileset" e coloque 8x8 (ou o tamanho que você usou) e dê OK.
Pronto. Agora nós temos nossos obstáculos. Entre na rm_teste, e distribua os obstáculos corretamente, colocando os tilesets um à um desejado e depois colocando um obj_obstáculo_alto ou obj_obstáculo_raso somente sobre cada um que possa ser um possível obstáculo para o jogador (somente o das bordas, não sobrecarregue o jogo colocando instâncias desnecessárias), combinando assim o obstáculo visual com o obtáculo real. Se quiser, crie um bg_teste para colocar como plano de fundo na sua rm_teste. Veja na foto um exemplo:
Apenas os Tilesets do bg_obstaculos, sem instâncias de objetos.
Com os obj_obstaculo_alto e obj_obstaculo_raso já colocados em seus respectivos lugares.
Lembre-se: o obj_obstaculo_alto não vai permitir que projéteis (como flechas e habilidades) passem por ele, e nem a visão inimiga, então coloque-o sobre árvores, pedras e coisas do gênero. O obj_obstáculo_raso vai permitir a passagem de projéteis, então coloque-os sobre rios, grades e coisas do gênero.

Por enquanto, não se preocupe em fazer muitas rooms. Nós ainda temos que criar todo o resto do jogo, como inimigos, itens e npcs. Deixe para criar os "mapas" quando dispor de todos os recursos. Agora vamos voltar aos nossos personagens.
4. Ataques e Habilidades:

Conceito:

Agora nós temos um mago, um guerreiro e um arqueiro. Cada um terá três formas de ataque: básico, à distância e especial. O ataque básico será de curta distância, danificando os inimigos à frente. O ataque à distância gastará algum tipo de munição, como flechas ou espírito. O especial vai utilizar uma espécie de energia que se acumula com o tempo ou quanto o personagem derrota inimigos. Para trocar de ataque, utilizaremos as teclas 1,2 e 3, respectivamente. 

Nós também criaremos Power Ups, itens que oferecerão temporáriamente super ataques para nossos pequenos soldados, só que mais à frente... um passo de cada vez.

Preparação:

Lembre-se que estes ataques são só um exemplo. Você pode trocar o obj_ataque_meteoro por obj_ataque_chuvadetrovoes, por exemplo. Fique tranquilo, depois que você entender como funciona esta mecânica, poderá criar quantos ataques você precisar.

Crie as seguintes Sprites: spr_flecha, spr_ataque_basico, spr_bola_de_fogo, spr_super_flecha, spr_super_golpe, spr_meteoro, spr_enfeite_splash, spr_enfeite_sanguejorrando, obj_enfeite_geral, spr_escudo, spr_arqueiro_ataque_0, spr_arqueiro_ataque_90, spr_arqueiro_ataque_180, spr_arqueiro_ataque_270, spr_guerreiro_ataque_0, spr_guerreiro_ataque_90, spr_guerreiro_ataque_180, spr_guerreiro_ataque_270, spr_mago_ataque_0, spr_mago_ataque_90, spr_mago_ataque_180, spr_mago_ataque_270.
Crie os seguintes Objetos: obj_ataque_geral, obj_projetil_geral, obj_ataque_flecha, obj_ataque_boladefogo, obj_ataque_superflecha, obj_ataque_meteoro, obj_ataque_basico, obj_ataque_supergolpe, obj_inimigo, obj_enfeite_splash, obj_enfeite_sanguejorrando, obj_escudo.
Crie os seguintes Scripts: script_ataque_setup, script_projetil_setup, script_ataque_geral, script_projetil_geral, script_ataque_geral, script_personagem_ataque, script_personagem_escolhadeataque, script_escudo, script_golpeespecial_guerreiro, script_golpeespecial_arqueiro, script_golpeespecial_mago.

Criando:

Agora temos tudo que precisamos para criar nossos ataques. Assim como os personagens, nós vamos criar um padrão utilizando o obj_ataque_geral e obj_projetil_geral e depois vamos distribuir atributos personalizados nos ataques, para que cada um reaja de forma diferente.

Novamente, vamos criar as variáveis iniciais dos nossos ataques utilizando o script_ataque_setup (para ataques corpo-à-corpo) e o script_projetil_setup (para ataque à longa distância), que deverão ficar assim:

- No script_ataque_setup:

//Setup básico dos ataques
{
    var_dano = obj_personagem.var_dano;
    image_speed =0.5;
}

- No script_projetil_setup:

// Setup básico de projéteis:
{
   direction = obj_personagem.var_face; //A direção da flecha vai ser a mesma da face do obj_personagem
   speed = var_velocidade;
   var_distancia = 90//Com uma duração de 90, o projetil durará 3 segundos. Aumente se quiser.
}

Agora coloque estes pequenos Scripts no Create Event do obj_ataque_geral e obj_projetil_geral, respectivamente. No Parent do obj_projetil_geral, coloque o obj_ataque_geral e coloque também um "Call event" no Create Event dele, afinal, ele também é um tipo de ataque. Ambos deverão ficar assim:
Pronto. Agora temos o setup dos nossos ataques. Agora vamos fazer os ataques funcionarem. Abra o script_ataque_geral e coloque o seguinte código:

//Ataque geral
{
    if place_meeting(x,y,obj_inimigo_geral)//Se um inimigo está sobre o ataque...
        {
         var_meu_alvo = instance_nearest(x,y,obj_inimigo_geral); //O alvo do ataque será o inimigo mais próximo, ou seja, o que está sob o ataque neste exato momento
     var_meu_alvo.energia -= var_dano; //Reduz a energia do inimigo mais próximo na mesma quantidade do dano. Lembre-se que não criamos essa variável no obj_inimigo ainda, portanto se rodar o jogo agora, ocorrerá um erro.
          
         instance_change(obj_enfeite_sanguejorrando, 1); //O ataque vira seu efeito de sangue jorrando.
        }
}

Coloque este script no Step Event do obj_ataque_geral.

Vamos trabalhar no obj_inimigo_geral mais à frente. Por enquanto deixe-o como está. Já o obj_enfeite_sanguejorrando, tente colocar uma Sprite com cerca de 5 subimagens representando um sangue progressivamente sumindo para dar um efeito de sangue sendo jorrado pelo inimigo. Agora coloque o Parent dele o obj_enfeite_geral. Já obj_enfeite_geral, crie um evento de Animation End ("other" > "animation end") e coloque uma folha de código normal apenas escrita instance_destroy(). Agora nossos efeitos visuais serão criados e automáticamente destruídos quando a animação deles acabarem.

Agora vamos programar o script_projetil_geral. Basicamente, os projeteis também vão levar o script_ataque_geral consigo (afinal, eles também são um tipo de ataque). No script_projetil_geral, vamos apenas fazer com que eles se destruam com o tempo (para não irem muito longe) e se destruam ao bater num obstáculo. Ficará o seguinte:

//Projétil geral
{
    var_distancia -=1; //Começa uma contagem regressiva da duração

  image_index = direction * image_number/360; //Esse código muda automáticamente para a subimagem correta de acordo com a direção do escudo.
//NOTA: SE VOCÊ TIVER O GM PRO, TROQUE O "IMAGE_INDEX" POR "IMAGE_ANGLE".
    if var_distancia <=0; //Quando a duração do projétil acabar...
        {
        speed =0; //Zera a velocidade
        instance_change(obj_enfeite_splash, 1); //Ele é destruído, virando um efeio visual básico
        }

    if place_meeting(x,y,obj_obstaculo_alto)//Se acontecer um choque com o obj_obstáculo_alto
          var_distancia =0;//A var_distancia é zerada, assim destruindo o projétil.
}

Para a seleção automática de subimagem funcionar corretamente de acordo com a direção do projétil, as Sprites de todos os projéteis devem ser um pouco diferente: você deverá fazer elas com 4 subimagens, cada uma com o projétil virado para uma direção diferente, começando para a direita (0º). Por exemplo:

Nota: Se você tiver o GM PRO, basta apenas fazer a primeira subimagem e usamos a função "image_angle".

Agora nós temos a base de nossos ataques. Logo faremos o nosso personagem atacar, mas antes precisamos criar os ataques que eles realmente vão ultilizar, como o obj_ataque_flecha e obj_ataque_boladefogo. Isso vai ser bem fácil, só precisamos dar uma pequena personalizada em cada um.

- Coloque as respectivas Sprites no obj_ataque_boladefogo, obj_ataque_flecha, obj_ataque_basico, obj_ataque_meteoro, obj_ataque_supergolpe, obj_ataque_superflecha.
- O Parent de obj_ataque_superflecha, obj_ataque_meteoro, obj_ataque_boladefogo, obj_ataque_flecha devem estar no obj_projetil_geral.
- O obj_ataque_basico e obj_ataque_supergolpe devem ter o Parent diretamente ligados ao obj_ataque_geral.

AJUDANDO A ENTENDER: "O obj_projetil_geral tem o Parent como obj_ataque_geral, logo todos não vão ser a mesma coisa?" - Na realidade, os nossos projéteis são, de fato, ataques. Logo nós queremos que os nossos projéteis se comportem como ataques, porém não queremos que todos os nossos ataques se comportem como projéteis. Por isso o obj_ataque_basico e obj_ataque_supergolpe são diretamente ligados ao obj_ataque_geral e não por meio do obj_projetil_geral.

Para dar as pequenas personalizadas que eu tanto digo, você pode fazer o seguinte: no Create Event de cada um dos super ataques, ou seja, o obj_ataque_supergolpe, obj_ataque_meteoro, obj_ataque_superflecha, coloque uma folha de código simples apenas escrito {dano = dano*2;}, obviamente não esquecendo do "Call Event" antes. Isso fará os nossos super ataques terem o dano dobrado.

Terminamos os nossos ataques. Vamos deixar os especiais para mais tarde. Agora vamos fazer com que o nosso personagem ataque, usando a tecla "K" (ou qualquer outra que você desejar). Como eu disse, nossos personagens terão três ataques: um corpo-à-corpo, outro à distância (no qual gasta munição no guerreiro/arqueiro e mana no mago) e outro especial (que gasta a barra de especial). Nós vamos trocar os ataques usando as teclas "1", "2" e "3".

Porém, antes de fazermos o nosso personagem atacar, vamos criar a variável "var_meu_ataque =0" (na qual vai indicar qual tipo de ataque está selecionado no momento). Coloque esta variável junto com as otras no script_jogador_setup_geral. Essa variável vai seguir a seguinte lógica:

Se var_meu_ataque for 0, estamos usando o ataque corpo-à-corpo.
Se var_meu_ataque for 1, estamos usando o ataque à distância.
Se var_meu_ataque for 2, estamos usando o golpe especial.

Agora alterando um à um os script_jogador_setup_guerreiro, script_jogador_setup_mago e script_jogador_setup_arqueiro, vamos criar as variaveis "var_meu_golpe", "var_meu_projetil" e "var_meu_script_especial".

- Para todos os três personagens coloque "var_meu_golpe = obj_ataque_basico". Isso quer dizer que, por padrão, os três personagens atacarão com o obj_ataque_basico futuramente.

- Agora a "var_meu_projetil" é um caso à parte. No script_setup_personagem_arqueiro, coloque "var_meu_projetil = obj_ataque_flecha" e no script_setup_personagem_mago coloque "var_meu_projetil = obj_ataque_boladefogo. O script_setup_personagem_guerreiro não deve atacar à distância, como a sua classe impõe, mas logo vamos criar um escudo para ele, que vai substituir essa função. Por enquanto, deixe "var_meu_projetil = obj_ataque_flecha" para evitar erros. Como nós deixamos ele sem munição no setup, nada vai acontecer.

- O "var_meu_script_especial" também são casos separados, porém facilmente distintos. No script_setup_personagem_guerreiro, coloque "var_meu_script_especial = script_golpeespecial_guerreiro", no script_setup_personagem_arqueiro, coloque "var_meu_script_especial = script_golpeespecial_arqueiro" e por fim, no script_setup_personagem_mago, coloque "var_meu_script_especial = script_golpeespecial_mago".

Agora vamos realmente fazer nossos personagens atacarem. No script_personagem_ataque, vamos fazer o seguinte código:

if keyboard_check(ord("K")) and var_ataque_tempo <=0 //Se a tecla "K" está sendo pressionada e não acabamos de efetuar um ataque ainda...
   {
    if var_meu_ataque =0//Se o nosso ataque selecionado é o corpo-à-corpo
       {
        var_ataque_tempo = 30//Vai mostrar que acabamos de atacar e não poderemos atacar novamente por 1 segundo.
       switch(var_face)//Vamos criar nosso ataque de acordo com a direção do personagem
               {
                case 0: instance_create(x+8,y,var_meu_golpe); break;
                case 90: instance_create(x,y-8,var_meu_golpe); break;
                case 180: instance_create(x-8,y,var_meu_golpe); break;
                case 270: instance_create(x,y+8,var_meu_golpe); break;
                }
           }
     if var_meu_ataque =1 and var_municao >0//Se o nosso ataque selecionado é à distância e tivermos munição...
       {
        var_ataque_tempo = 30//Vai mostrar que acabamos de atacar e não poderemos atacar novamente por 1 segundo.
        var_municao -=1//Reduzimos uma munição do total que temos
        switch(var_face)//Vamos criar nosso projétil de acordo com a direção do personagem
               {
                case 0: instance_create(x+8,y,var_meu_projetil); break;
                case 90: instance_create(x,y-8,var_meu_projetil); break;
                case 180: instance_create(x-8,y,var_meu_projetil); break;
                case 270: instance_create(x,y+8,var_meu_projetil); break;
                }
           }
     if var_meu_ataque =2 and var_especial =100//Se nosso ataque selecionado é o golpe especial e temos pontos de especial o suficiente...
           {
        var_ataque_tempo = 30//Vai mostrar que acabamos de atacar e não poderemos atacar novamente por 1 segundo.
           var_especial =0//Reduz o contador de pontos de especial que temos.
           script_execute(var_meu_script_especial);//Executa o script especial que colocamos em cada personagem.
           }
}

Nosso script de ataque está pronto. Novamente, coloque-o no Step Event do obj_personagem junto aos outros scripts. Agora vamos habilitar a troca de ataques (utilizando as teclas "1", "2" e "3" como já dito) usando o script_personagem_escolhadeataque. Basta colocar este código:

{
if keyboard_check(ord("1"))//Se a tecla 1 foi pressionada
    {
    var_meu_ataque =0//Troca para o ataque corpo à corpo.
    }
if keyboard_check(ord("2")) and var_municao >0//Se a tecla 2 foi pressionada e temos munição sobrando...
    {
    var_meu_ataque =1//Troca para o ataque à distância.
    }
if keyboard_check(ord("3")) and var_especial =100//Se a tecla 3 foi pressionada e temos pontos de especial maximizados no momento...
    {
    var_meu_ataque =2//Troca para o ataque especial.
    }
}

Agora coloque o script_personagem_escolhadeataque no Step Event do obj_personagem também junto aos outros scripts. Resta ainda trabalhar no especial dos três personagens, nos quais vamos fazer novos ataques com um pouco de criatividade.

Vamos trabalhar primeiro no obj_escudo do obj_personagem_guerreiro. Este, por sua vez, não terá nenhum Parent pois não se deve comportar nem como ataque, muito menos como um projétil, apesar de utilizar o mesmo código. A Sprite dele também deve seguir o padrão dos projéteis:
Nota: mais uma vez, se você tiver o GM PRO, faça apenas a primeira subimagem e já será o suficiente!

Seguindo, vamos trabalhar no script_escudo. Temos milhares de possibilidades, mas a que eu acho mais interessante é a seguinte:

//Script do obj_escudo, no qual substituirá o ataque à distância do obj_personagem_guerreiro.
{
obj_personagem.var_ataque_tempo =15;//Coloca constantemente um Delay no jogador, no qual impede que ele ataque enquanto o escudo está ativo. Como essa variável sempre será renovada, quando o obj_escudo ser destruído o obj_personagem_guerreiro ainda ficará 0,5 segundo sem poder atacar nem criar um novo escudo.

direction = obj_personagem.var_face; //A direção do escudo será a mesma da face do jogador.

image_index = direction * image_number/360; //Esse código muda automáticamente para a subimagem correta de acordo com a direção do escudo.
//NOTA: SE VOCÊ TIVER O GM PRO, TROQUE O "IMAGE_INDEX" POR "IMAGE_ANGLE". 

switch(direction)//Agora vamos fazer o escudo ficar sempre na frente do personagem corretamente...
   {
    case 0:
              x = obj_personagem.x+4;
              y = obj_personagem.y;
              break; 
    case 90:
              x = obj_personagem.x;
              y = obj_personagem.y-4;
              break;
    case 180:
              x = obj_personagem.x-4;
              y = obj_personagem.y;
              break;
    case 270:
              x = obj_personagem.x;
              y = obj_personagem.y+4;
              break;    
    default:
              x = obj_personagem.x;
              y = obj_personagem.y;
              break;   
   }

if place_meeting(x,y,obj_inimigo)//Se existir um inimigo colidindo com o escudo...
    {
     obj_personagem.var_municao -=1; //Retira uma munição, ou seja, uma carga do escudo do personagem
     var_meu_alvo = instance_nearest(x,y,obj_inimigo); //Marca o alvo, ou seja, o inimigo colidindo...

     var_meu_alvo.speed = 2; //Adiciona uma velocidade no inimigo, para empurrar ele
     var_meu_alvo.direction = direction; //O inimigo é empurrado na mesma direção do escudo
     var_meu_alvo.friction = 0.2; //Isso faz com que o inimigo pare lentamente, criando assim um efeito de empurrão mais realista e sólido.
    }

if !keyboard_check(ord("K")) or obj_personagem.var_municao <=0 or !obj_personagem.var_meu_ataque =1//Se a tecla "K" deixa de ser pressionada ou a munição, ou seja, a carga do escudo do jogador acabar ou outro ataque for selecionado...
    {
     instance_destroy();
    }
}

AJUDANDO A ENTENDER: "O que é 'Default' no switch?" - "Default" é o que acontece quando nenhuma condição do Switch foi saciada. Tomando como exemplo o switch acima, supomos que por algum motivo a 'direction' do escudo ficasse 45(º). Nesse caso, o Default vai acontecer. Claro que, nesse caso em específico, è quase impossível disso acontecer, mas colocamos apenas para evitar grandes bugs.

Coloque este código no Step Event do obj_escudo. O escudo deverá funcionar corretamente, sendo destruído quando as suas cargas acabarem ou a tecla "K" for liberada ou quando o jogador seleciona outro ataque. Agora não se esqueça de colocar a "var_meu_projetil = obj_escudo" no script_personagem_setup_guerreiro e não se esqueça de alterar a "var_municao" de 0 para algum valor razoável, como 40.

Agora vamos trabalhar nos três especiais dos personagens, utilizando os recursos que já criamos de uma forma diferente. Eu farei os três especiais da forma que eu achar melhor, mas você pode tentar usar o conhecimento que você obteve até agora para tentar algo novo.

- No script_golpeespecial_arqueiro:
//Golpe especial do personagem arqueiro, "Círculo de flechas". O arqueiro vai atacar flechas em oito direções diferentes em volta dele.
{
var_direcao = 0
repeat(8)//Repetir 7 vezes o seguinte script, ou seja, criando 8 flechas...
    {
     var_ataque = instance_create(x,y,obj_ataque_superflecha)
     var_ataque.direction = var_direcao
     var_ataque.speed = 4

    var_direcao +=45//A próxima flecha será num ângulo de 45º à mais que a anterior
    }
}

- No script_golpeespecial_mago:
//Golpe especial do personagem mago, "Onda de meteoros". O mago vai lançar quatro meteoros rápidos em formato de cruz e quatro meteoros lentos em forma de X. Nesse processo, ele ainda vai curar uma parcela da sua energia.
{
   var_energia +=round(var_energia_max/3)//Cura energia de acordo com a energia máxima do mago, nesse caso, 33% da energia total.
  
var_direcao = 0
var_ataque_velocidade =4
repeat(8)//Repetir 7 vezes o seguinte script, ou seja, criando 8 meteoros...
    {
     var_ataque = instance_create(x,y,obj_ataque_meteoro)
     var_ataque.direction = var_direcao
     var_ataque.speed =var_ataque_velocidade

     if var_ataque_velocidade =4//Se a velocidade do último tiro foi 4...
         var_ataque_velocidade =2;//Altera para 2
     else
         var_ataque_velocidade =4;//Se não, agora é 4.
     var_direcao +=45//O próximo meteoro será num ângulo de 45º à mais que o anterior
    }
}

- No script_golpeespecial_guerreiro:
//Golpe especial do personagem guerreiro, "Ataque mega-ofensivo". O guerreiro vai se teleportar ao inimigo mais próximo atacando-o pesadamente. Isso só vai acontecer quando não ouver nenhum obstáculos entre os dois.
{
var_meu_alvo = instance_nearest(x,y,obj_inimigo);
var_especial = 100;//Reseta o valor do especial, para impedir de gastá-los caso o especial não dê certo.

if distance_to_object(var_meu_alvo) <=64//Estabelece um limite de distância entre o alvo, para impedir que a àrea do efeito do especial seja infinito.
if !collision_line(var_meu_alvo.x,var_meu_alvo.y, obj_personagem.x, obj_personagem.y, obj_parede_geral, 0, 1)//Se não ouver nenhum obstáulo no caminho entre o jogador e o inimigo mais próximo...
    {
     instance_create(x,y,obj_enfeite_splash);//Cria um pequeno efeito visual antes de teleportar-se
     x = var_meu_alvo.x;
     y = var_meu_alvo.y;
     
     repeat(5)
        instance_create(var_meu_alvo.x,var_meu_alvo.y,obj_ataque_supergolpe);//Cria 5 super-ataques no inimigo, ou seja, 10x mais dano.
        
     var_meu_alvo.speed =4//Cria um sistema
     var_meu_alvo.friction = 0.2//de empurrão
     var_meu_alvo.direction = var_face//no inimigo..
     
     var_especial = 0;//O especial deu certo, então zeramos os pontos de especial.
    }
}
Agora todos os nossos especiais estão prontos. Assim que obtermos 100 pontos de especial com nossos personagens, poderemos selecionar o ataque da tecla "3" e pressionarmos "K" para utilizá-los. Ainda temos que encontrar um meio de fazermos o nosso personagem ganhar tais "pontos de especial", mas isso faremos mais à frente quando criarmos de fato nossos inimigos.
Mas espere um pouco... nosso personagem pode até estar atacando corretamente, mas ainda como vamos fazer para que ele ataque visualmente, usando as Sprites corretas? Bem, primeiro, vamos "ensinar" ao jogo quais são as Sprites de nossos personagens atacando.

- No script_personagem_setup_guerreiro, adicione:
    var_sprite_0_atacando = spr_guerreiro_ataque_0;
    var_sprite_90 = spr_guerreiro_ataque_90;
    var_sprite_180 = spr_guerreiro_ataque_180;
    var_sprite_270 = spr_guerreiro_ataque_270;

Faça o mesmo script_personagem_setup_arqueiro e no script_personagem_setup_mago, trocando apenas a parte do "guerreiro" por "arqueiro/mago".

Agora lembra-se que já temos um script que muda automáticamente as sprites do personagem? Pois bem, vamos ter que fazer algumas alterações nele. Abra novamente o script_personagem_sprite e troque a última parte marcada do script para a seguinte:

//Configurar a sprite correta de acordo com a direção, ou seja, a face do personagem
if var_ataque_tempo>0 //Se ele está atacando... 
switch(var_face)
    {
    case 0: sprite_index=var_sprite_0_ataque; break;
    case 90: sprite_index=var_sprite_90_ataque; break;
    case 180: sprite_index=var_sprite_180_ataque; break;
    case 270: sprite_index=var_sprite_270_ataque; break;
    }
else//Se não...
switch(var_face)
    {
    case 0: sprite_index=var_sprite_0; break;
    case 90: sprite_index=var_sprite_90; break;
    case 180: sprite_index=var_sprite_180; break;
    case 270: sprite_index=var_sprite_270; break;
    }

Pronto. Novamente, abra a rm_teste e tente efetuar alguns ataques dos três tipos (o especial pode não dar certo, pois o var_especial começa em 0 e não 100, então não se esqueça de alterar isso se quiser testá-lo também). Caso tudo deu certo até agora, vamos ao passo seguinte.
5. Itens, Equipamentos e PowerUps:

Conceito:

Chegou a hora de criarmos nossos equipamentos, itens, tesouros, poções e coisas desse tipo. Junto com o sistema de atributos, nossos equipamentos vão interferir na Energia máxima, Mana máxima e Dano do nosso personagem. Com nosso dinheiro, iremos interagir com NPCs para comprar equipamentos e outros itens. Lembra-se que no setup dos personagens já criamos um "sistema" de equipamentos? Também é hora de fazer ele funcionar corretamente. Além do mais, temos que fazer funcionar o sistema de super-ataques de nossos personagens utilizando Power Ups temporários. Com um passo de cada vez, iremos cumprir todos esses objetivos!

Preparação:

Crie as seguintes Sprites: spr_item_moeda, spr_item_tesouro, spr_item_diamante, spr_item_pocaodeenergia, spr_item_pocaodemana, spr_power_up, spr_equip_espadacomum, spr_equip_espadadourada, spr_equip_espadadecristal, spr_equip_arcocomum, spr_equip_arcodourado, spr_equip_arcodecristal, spr_equip_cajadocomum, spr_equip_cajadodourado, spr_equip_cajadodecristal, spr_equip_armaduradepano, spr_equip_armaduradeferro, spr_equip_armaduradourada.
Crie os seguintes Objetos: obj_item, obj_item_auto, obj_item_press, obj_equip_arma, obj_equip_armadura, obj_item_pocaodeenergia, obj_item_pocaodemana, obj_arma_comum, obj_arma_dourada, obj_arma_cristal, obj_armadura_pano, obj_armadura_ferro, obj_armadura_dourada, obj_power_up, obj_item_moeda, obj_item_tesouro, obj_item_diamante, obj_enfeite_mostradordeponto.
Crie os seguintes Scripts: script_item_auto, script_item_press, script_mostradordeponto, script_power_up
Crie os seguintes efeitos sonoros: snd_power_up

Criando:

Nós vamos criar dois tipos de itens. Um no qual é coletado automáticamente quando o jogador passa por cima, e outros que são coletados somente ao pressionar uma tecla específica.
Primeiro, vamos adicionar os Parents corretamente:
- obj_item_auto e obj_item_press com Parent no obj_item.
- obj_item_arma, obj_item_armadura, obj_item_pocaodeenergia, obj_item_pocaodemana com Parent no obj_item_press.
- obj_item_moeda, obj_item_tesouro e obj_item_diamante com Parent obj_item_auto.
- obj_arma_comum, obj_arma_dourada, obj_arma_cristal com Parent no obj_equip_arma.
- obj_armadura_pano, obj_armadura_ferro, obj_armadura_dourada com Parent no obj_equip_armadura.

Pronto. Agora vamos trabalhar nos scripts. O primeiro deles será o obj_item_auto, que é um dos mais simples. Deverá ficar assim:

{
if distance_to_object(obj_personagem) <=2
   {
   move_towards_point(obj_personagem.x, obj_personagem.y, 2);//Cria um efeito simples porém interessante em que o item vai na direção do jogador quando ele se aproxima demais, dando a impressão que ele atrai o item.
   }

if place_meeting(x,y, obj_personagem)
   {
    alarm[0] =1;//Como nós teremos diversos itens variados e seus efeitos não serão os mesmos, precisamos colocá-los separadamente. Nesse caso, o sistema de alarmes vai nos ajudar.

   instance_destroy();
   }
}

Coloque este script naturalmente no Step Event do obj_item_auto. O próximo script será o script_item_press, que deverá ficar parecido com o script acima, além de ser um pouco menor:

{
if place_meeting(x,y,obj_personagem) and keyboard_key_press(ord("L"))//Usarei a tecla "L" como tecla de ação secundária. Você pode trocar por outra se desejar.
    {
     alarm[0] =1;
    
     instance_destroy();
    }
}

Agora coloque no Step Event do obj_item_press.

AJUDANDO A ENTENDER: "O que é e como e funciona um Alarm?" - O evento Alarm acontece quando o tempo que você determina nele acaba. Por exemplo, se eu colocar alarm[0]=60, tudo que está no evento do Alarm 0 irá ocorrer ao passar 2 segundos (60 steps). Vale lembrar que isso não é igual ao Step Event. As ações do Alarm só ocorrem uma vez, e você deverá colocar o contador novamente se quiser que aconteçam novamente. Caso você precise de uma ação instantânea, coloque Alarm["x"]=1, por exemplo. Você também pode usar os alarm para que uma ação ocorra, por exemplo, sempre à cada 3 segundos. Basta apenas colocar Alarm[0]=90 no próprio Alarm 0, e o ciclo vai se repetir infinitamente.

Para configurar as Armas e as Armaduras, não usaremos nenhum script, porém uma folha de código simples no lugar. O motivo disso é que será algo simples demais. No alarm[0] ("create event" > "alarm" > "alarm 0") coloque uma folha de código em ambos com os seguintes códigos:

- No obj_equip_arma:
{
    obj_jogador.var_equipamento_arma_nome =var_nome;
    obj_jogador.var_equipamento_arma =var_ataque;
}

- No obj_equip_armadura:
{
    obj_jogador.var_equipamento_armadura_nome =var_nome;
    obj_jogador.var_equipamento_armadura =var_defesa;
}
Assim como nos ataques, nós vamos dar as pequenas personalizadas em cada armadura e arma separadamente, colocando uma folha de código simples no Create Event de cada uma e editando os seus valores, lembrando que vamos criar um pequeno sistema para o Sprite da arma mudar automáticamente de acordo com o personagem:

- No obj_arma_comum:
//Arma comum setup
if instance_exists(obj_personagem_guerreiro)
    {
    var_nome = "Espada comum";
    sprite_index = spr_arma_espadacomum;
    }
if instance_exists(obj_personagem_mago)
    {
    var_nome = "Cajado comum";
    sprite_index = spr_arma_cajadocomum;
    }
if instance_exists(obj_personagem_arqueiro)
    {
    var_nome = "Arco comum";
    sprite_index = spr_arma_arcocomum;
    }
    var_ataque =1;
}

- No obj_arma_dourada:
//Arma dourada setup
if instance_exists(obj_personagem_guerreiro)
    {
    var_nome = "Espada dourada";
    sprite_index = spr_arma_espadadourada;
    }
if instance_exists(obj_personagem_mago)
    {
    var_nome = "Cajado dourado";
    sprite_index = spr_arma_cajadocomum;
    }
if instance_exists(obj_personagem_arqueiro)
    {
    var_nome = "Arco dourado";
    sprite_index = spr_arma_arcocomum;
    }
    var_ataque =2;
}

- No obj_arma_cristal:
//Arma de cristal setup
if instance_exists(obj_personagem_guerreiro)
    {
    var_nome = "Espada de cristal";
    sprite_index = spr_arma_espadadecristal;
    }
if instance_exists(obj_personagem_mago)
    {
    var_nome = "Cajado de cristal";
    sprite_index = spr_arma_cajadodecristal;
    }
if instance_exists(obj_personagem_arqueiro)
    {
    var_nome = "Arco de cristal";
    sprite_index = spr_arma_arcodecristal;
    }
    var_ataque =3;
}

As armaduras vão seguir o mesmo padrão. Como nós não vamos diferenciar as armaduras de acordo com os personagens, será um pouco mais simples:

- No obj_armadura_pano:
//Armadura de pano setup
{
    var_nome = "Armadura de pano";
    var_defesa =1;
}

- No obj_armadura_ferro:
//Armadura de ferro setup
{
    var_nome = "Armadura de ferro";
    var_defesa =2;
}

- No obj_armadura_cristal:
//Armadura de cristal setup
{
    var_nome = "Armadura dourada";
    var_defesa =3;
}

Feito. Como você com certeza percebeu com o trabalho até agora, tudo se trata de ações em cadeia, onde tudo leva à outra coisa, sempre seguindo um mesmo padrão.

Antes de mais nada, vamos acrescentar um pequeno código no script_personagem_arrumador, no qual fará o nome da arma/armadura ser "Nenhuma" caso o personagem não tenha uma arma. Acrescente o seguinte:

if var_equipamento_arma =0
   var_equipamento_arma_nome ="Nenhuma";
if var_equipamento_armadura =0
   var_equipamento_armadura_nome ="Nenhuma";

Isso fará uma pequena mudança quando criarmos a interface do jogo, e afinal, uma correção automática é sempre bem-vindo (é uma coisa à menos para nos preocuparmos). Agora vamos fazer nosso dinheiro, tesouros, moedas e diamantes, que será extremamente simples. Na verdade, nós já fizemos parte disso, e o esquema será exatamente o mesmo. No alarm[0] do obj_item_auto, coloque:

//Setup de itens de dinheiro
{
    obj_personagem.var_moedas += var_quantidade;//Adiciona o dinheiro ao jogador
    var_enfeite = instance_create(x,y,obj_enfeite_mostradordeponto);//Cria um enfeite que será programado para mostrar quantos pontos o jogador obteve ao pegar o item.
    var_enfeite.var_quantidade = var_quantidade;//O obj_enfeite criado copia essa variável.
}

Agora vamos programar o obj_enfeite_mostradordeponto para que ele mostre quantos pontos o obj_personagem obteve ao pegar o item, de uma forma que os pontos mostrados subam lentamente na tela e desapareçam depois de alguns segundos. Ele será um pequeno fragmento da nossa interface, ou seja, ele utilizará o Draw Event. No script_mostradordeponto, faça o seguinte código:

//Mostra quantos pontos o jogador obteve
{
   draw_text(x,y,"+"+string(var_pontos))
}

Coloque este script no Draw Event do obj_enfeite_mostradordeponto. No Create Event, coloque {alarm[0] = 30;}. No Alarm[0], coloque {instance_destroy();}.Agora no Step Event, coloque uma folha de código simples apenas com {y+=1;}.

Vamos começar a trabalhar com os Power Ups. Basicamente eles serão itens especiais que almentam o poder do personagem por um certo período de, digamos, 30 segundos. No script_power_up, podemos colocar algo como:

if place_meeting(x,y,obj_jogador)
{
  obj_personagem.var_power_up = 900//30 steps por segundo x 30 segundos = 900
  sound_play(snd_power_up);//Efeito sonoro
  instance_destroy()//Destrói o item após o efeito.
}

Agora vamos ter que fazer pequenas alterações no obj_personagem, novamente, para que o Power Up funcione.

- No script_personagem_setup_geral, acrescente:
  var_power_up =0;

- No script_personagem_setup_guerreiro, acrescente:
  var_meu_supergolpe = obj_ataque_supergolpe;

- No script_personagem_setup_arqueiro, acrescente:
  var_meu_supergolpe = obj_ataque_superflecha;

- No script_personagem_setup_mago, acrescente:
  var_meu_supergolpe = obj_ataque_meteoro;

Agora faremos uma pequena mudança no script_personagem_ataque. Troquer a primeira parte do script (var_meu_ataque = 0) pela seguinte:

    if var_meu_ataque =0//Se o nosso ataque selecionado é o corpo-à-corpo
       {
        var_ataque_tempo = 30
        if  var_power_up <=0//Se o contador de super ataque está zerado.
        switch(var_face)//Vamos criar nosso ataque de acordo com a direção do personagem
               {
                case 0: instance_create(x+8,y,var_meu_golpe); break;
                case 90: instance_create(x,y-8,var_meu_golpe); break;
                case 180: instance_create(x-8,y,var_meu_golpe); break;
                case 270: instance_create(x,y+8,var_meu_golpe); break;
                }
        else
        switch(var_face)//Vamos criar nosso superataque de acordo com a direção do personagem
               {
                case 0: instance_create(x+8,y,var_meu_supergolpe); break;
                case 90: instance_create(x,y-8,var_meu_supergolpe); break;
                case 180: instance_create(x-8,y,var_meu_supergolpe); break;
                case 270: instance_create(x,y+8,var_meu_supergolpe); break;
                }
           }

No script_personagem_arrumador, vamos acrescentar o seguinte:

if var_power_up >0
   var_power_up -=1;

Pronto. Agora nosso personagem está apto à ter super-ataques por um curto período. Se quiser dar uma segunda olhada, vá no script_personagem_ataque e veja que o obj_personagem fará super-golpes quando a var_power_up está acima de 0, e no script_personagem_arrumador diminuirá constantemente em 1 o var_power_up até que ele se esgote. Tudo está se encaixando. Logo, quando programarmos os inimigos, faremos com que os mais poderosos tenham pequenas chances de deixar tal Power Up cair.

Vamos para o próximo passo, no qual vamos nos aprofundar mais nos atributos do personagem (dano, defesa, energia, mana) e também no nível e experiência, como qualquer outro RPG comum.
6. Fazendo funcionar os atributos do personagem:
Conceito:

Os atributos do personagem é um elemento presente em qualquer tipo de RPG. 'Força', 'Inteligencia', 'Vitalidade', 'Destreza', 'Sorte', e outros tantos que você pode acrescentar. Neste projeto vamos trabalhar com algo mais simples, utilizando apenas quatro atributos que irão interferir no combate: Força, Deztreza, Vitalidade e Inteligência.

No início do nosso projeto, nós já atribuímos estes recursos ao nossos personagens (nos script_jogador_setup_x), mas eles eram apenas representativos. Vamos fazê-los com que funcionem realmente e tenham sinergia com o nível e com o equipamento dos nossos personagens.

Futuramente vamos criar formas diferentes do personagem aprimorar seus atributos, como por exemplo completar algumas missões.

Preparação:

Crie os seguintes scripts: script_personagem_mago_atributos, script_personagem_guerreiro_atributos, script_personagem_arqueiro_atributos, script
Crie os seguintes efeitos sonoros: snd_levelup

Método:

Este será um passo muito simples. Lembre-se que cada personagem possui uma sinergia maior com um atributo diferente. Por exemplo, a força agirá melhor com o guerreiro assim como um mago ficará mais eficaz com a inteligência alta do que os outros personagens.

No script_personagem_mago_atributos, coloque:
{
    var_dano = (var_equipamento_arma*5 + var_inteligencia*2);//O mago tem seu dano baseado em inteligência
    var_mana_max = (var_inteligencia*7);//Demos ao mago um destaque maior à mana
    var_energia_max = (var_vitalidade*3);//Demos um mago uma fragilidade maior quanto aos outros personagens, para compensar dano em potencial maior
}

No script_personagem_guerreiro_atributos, coloque:
{
    var_dano = (var_equipamento_arma*5 + var_forca*2);//O guerreiro tem seu dano baseado em força
    var_mana_max = (var_inteligencia);//Demos ao guerreiro o pior potencial para mana
    var_energia_max = (var_vitalidade*10);//Demos ao guerreiro a melhor resistência possível
}

No script_personagem_arqueiro_atributos, coloque:
{
    var_dano = (var_equipamento_arma*5 + var_destreza*2);//O arqueiro tem seu dano baseado na destreza
    var_mana_max = (var_inteligencia*3);//Demos ao arqueiro uma mana mediana
    var_energia_max = (var_vitalidade*5);//Demos ao arqueiro uma resistência mediana
}

Agora que os atributos estão um pouco mais consistentes, coloque, como de costume, estes scripts no Step Event em seus respectivos personagens. Agora vamos mexer novamente no script_personagem_arrumador.

Vamos fazer com que o personagem evolua de nível ao atingir uma certa quantidade de pontos de experiência. Esses pontos serão obtidos ao abater inimigos, e toda vez que o personagem ganha um nível, ele ganha alguns pontos de atributos baseados em sua classe e o limite para evoluir ao próximo inimigo se torna maior.

A formula que iremos usar para indicar quantos pontos o jogador deve acumular para avançar ao próximo nível será (pode ser alterada de acordo com o seu gosto):
(var_nivel*var_nivel)*17

No script_personagem_arrumador, acrescente:
if var_experiencia >= (var_nivel*var_nivel)*17
{
    var_experiencia -=(var_nivel*var_nivel)*17;//Retira os pontos de experiência já ganhos
    var_nivel +=1;//Avança um nível

    //Adiciona um ponto para cada atributo do personagem. Lembre-se que as classes se comportam diferentemente perante cada atributo.
    var_atributo_forca +=1;
    var_atributo_destreza +=1;
    var_atributo_inteligencia +=1;
    var_atributo_vitalidade +=1;

    //Prêmios adicionais
    var_energia = var_energia_max;//Como premio adicional, o jogador é curado ao máximo
    var_mana = var_mana_max;//Como premio adicional, a mana do jogador é restaurada ao máximo
    var_especial = 100//Como último prêmio adicional, a barra do golpe especial do jogador é preenchida

    //Efeito sonoro
    sound_play(snd_levelup);//Toca um efeito sonoro para indicar ao jogador
}

Este foi um passo bem simples, porém importante. Ele terá uma função muito importante durente o combate do jogo. Para que isso aconteça, nós precisamos de inimigos para batalharmos. E eu acho que este é o melhor momento.

[EM CONSTRUÇÃO]

37 comentários:

  1. Muito bom material!
    Gostaria de conversar sobre o assunto, poderia entrar em contato comigo!
    Fantastico o seu BLOG!

    ResponderExcluir
  2. SUPER FANTÁSTICO!!!!! Seu blog tem um ótimo conteúdo. A propósito, estou com um error na parte 4. O meu personagem não está soltando os projéteis. ;S
    Se poder me ajudar ;P

    ResponderExcluir
  3. rapaz continue assim muito bom, uma pergunta, precisa por o script_jogador_setup_geral, no create do obj_personagem ?

    ResponderExcluir
    Respostas
    1. Por quê este script é quem define todas as variáveis em que nós vamos trabalhar. Nós "ensinamos" ao programa o que ele terá que fazer, se não iria dar erro de script.

      Excluir
    2. Cara tô seguindo seu tuto faz 3 dias ja mas agora na parte dos ataques


      {
      var_dano = obj_personagem.dano;

      {
      direction = obj_personagem.face;
      o GM nao reconhece as variaveis dano e face tô perdido

      Excluir
    3. Acho que foi um erro da minha parte. Deveria estar assim:

      {
      var_dano = obj_personagem.var_dano;

      {
      direction = obj_personagem.var_face;

      eu esqueci de colocar o "var_" na frente do "face" e o "dano". Arrume desta maneira e acho que vai funcionar. ^^

      Excluir
    4. Só pra deixar claro, este "var_" é apenas uma maneira de organizar o projeto, para o próprio programador saber que o "var_dano" é uma variável e não um Sprite, Som, Objeto, etc.

      Excluir
    5. agora reconheceu obrigado pela ajuda=],


      o speed= var_velocidade;
      eu tambem troquei por um numero que ele nao reconhecia, agora o problema é que ele dá somente um ataque seja meele ou ranged =/, e nao é prob com o var_munição no debug ele desconta certinho

      Excluir
    6. http://www.4shared.com/rar/LJnps8zr/aprendizado_GM.html?

      taí o link de até onde eu cheguei =/

      Excluir
    7. Eu tentei dar uma olhada, mas este projeto está na versão 8.1 do Game Maker e o meu GM é a 8.0, então dá erro ao carregar as informações do arquivo D:

      Excluir
  4. Olá Doritos, comecei a criar um rpg seguindo como base todos os seus passos,
    caso surjem duvidas, voce ainda acompanha este blog ainda?

    Estou usando o GM 8.1 Standard

    ResponderExcluir
    Respostas
    1. Sim cara, acesso todos os dias para esclarecer dúvidas.

      Excluir
  5. Esteban Damián Barrea12 de setembro de 2013 00:49

    Olá Doritos desculpa a pergunta mais ja tem planejado quando vai continuar com o tuto ? :D
    Abraços ^^

    ResponderExcluir
    Respostas
    1. Este, eu não pretendo. Acho que querer ensinar tudo em um só guia de forma massante não só é cansativo como também problemático. Em breve eu farei novos guias sobre como criar um jogo, mas estes serão por partes (Parte 1 - fazendo o personagem andar, Parte 2 - inimigos... ).

      Excluir
    2. Esteban Damián Barrea14 de setembro de 2013 13:11

      Muito boa ideia Doritos mt bom o teu blog ^^

      Excluir
  6. Esteban Damián Barrea12 de setembro de 2013 23:04

    Doritos estou com problemas segui os teus pasos mais na hora de atirar uma flecha ela sai rodando ou seja ele carrega todas a imagens da flecha e logo apos lançar a flecha o meu personagem fica fazendo o ataque basico e não para ou seja ele fica batendo constantemente outro problema e que minha flecha não tem consegue se destruir nem por tempo nem se bater em um obstaulo alto ou seja ela segue girando e andando na mesma direção você poderia me ajudar (o meu ataque basico são socos feitos com imagens sequenciais)

    ResponderExcluir
    Respostas
    1. Sem o código fica muito difícil descobrir o que deu errado.

      Tente checar se:
      - o Obj_Flecha está com o Parent no Obj_Projétil_Geral

      - o Obj_Flecha possui o "Call Parent" no início de todos os seus eventos (Create Event, Step Event, Collision Event, etc).

      Excluir
    2. Esteban Damián Barrea15 de setembro de 2013 16:05

      Doritos muito obrigado deu certo a flecha ^^

      Excluir
  7. Respostas
    1. 8 Pixels de largura e 8 Pixels de altura (oito por oito)

      Excluir
  8. Doritos duas perguntas , vc ja finalizou algum jogo de rpg com GameMarker? e se é possivel colocar ele pra galera baixar ^^ , obraços broder seu tutorial esta muito bom.

    ResponderExcluir
    Respostas
    1. Os que eu finalizei, nunca cheguei a publicar. Eles eram bem simples, embora tenham dado bastante trabalho. Atualmente eu estou trabalhando em outro mas não é nada fácil terminar um jogo sozinho :(

      Excluir
  9. Está excelente! Mal posso esperar pelo resto. Obrigada, está sendo útil.

    ResponderExcluir
    Respostas
    1. Obrigado :), mas eu não sei se vou continuar esse guia.

      Excluir
  10. Parabéns, pelo tutorial e o blog! Estou também na torcida pela continuação!

    ResponderExcluir
  11. olha vo seguir tutorial pra ver se adianto umpoco do meu "projeto" em js/html '-'

    ResponderExcluir
  12. cara, o tutorial ta muito util, devia continuar!

    ResponderExcluir
    Respostas
    1. Talvez eu inicie algum outro. Achei esse muito pesado, especialmente para quem quer começar!

      Excluir
    2. Ok, o tutorial esta muito bom, mas seria melhor usar imagens como referencia no outro que vai fazer, pois quando fui fazer os sprites fiquei meio confuso.

      Excluir
    3. Sim, também acho que imagens são essenciais! Obrigado pela dica.

      Excluir
  13. obrigado!tem um jeito de fazer um tutorial de pokemon no game maker?

    ResponderExcluir
  14. Cara, não sei se você acompanha esse blog ainda, mas estou com um problema no script_projetil_geral, eu copiei ele do jeito que está aqui, mas quando vou rodar o jogo ele diz que o "if" da linha 7 está colocado de modo errado :/

    ResponderExcluir
  15. doritos como vc fez pra desenhar os personagens em 8X8 eu tentei mas n deu mt certo ficou horrivel(vlw ajudou bastante e é mt bom o seu site)recomenda algum site para criar alguns jogos para iniciantes?

    ResponderExcluir
  16. Doritos não entendi o primeiro passo pq o spr_mask ficou todo branco? posso usar minha propria sprite do meu personagem ????

    ResponderExcluir

Dúvidas? Pergunte!
Ajudou? Agradece!
:]