[Tutorial] Máquina de estados finitos

Ver o tópico anterior Ver o tópico seguinte Ir em baixo

Qualidade [Tutorial] Máquina de estados finitos

Mensagem por saim em Seg 21 Fev 2011, 11:20

Nome: Máquina de estados Finitos (Finite State Machines)
Descrição: Pretendo traduzir, aqui, um tutorial que uso com freqüência e notei que não tem correlato aqui na comunidade. O tutorial não é meu, só estou traduzindo. Créditos devem ir para o Ace, de outra comunidade.
O original pode ser encontrado aqui (pode ser necessário cadastrar).
Tenho muitos comentários a fazer, porém o tutorial é extenso e cheio de suspense antes da apresentação do script, então pode ficar chato ler uma introdução antes da introdução original.
Embora o tutorial não seja meu, me disponho a esclarecer qualquer dúvida com relação a ele.
Nível de dificuldade: intermediário/avançado
Requerimentos: Uso GM8, deve funcionar em GM7. Lite (apesar do exemplo no final requerer pro).
Desenvolvimento:

Introdução à máquina de estados finitos:

O que é uma “máquina de estados finitos”, você pergunta?
Bem, é exatamente o que parece. Uma “máquina” (ou sistema) que permite que algo “exista” num estado “finito” (singular).
Provavelmente, você está se perguntando qual a utilidade disso.A verdade é que serve pra tudo! Na verdade, muitos, se não TODOS os jogos desenvolvidos profissionalmente por grandes estúdios usam essas máquinas. Por quê? Porque são simples, modulares e muito eficientes de se usar para programadores de jogos profissionais e novatos.
Designers de jogos vêm de todos os cantos do mundo e, mesmo aqueles com experiência em programação tendem a não conhecer todos os aspectos do hardware e/ou linguagem que estão usando no projeto atual, então o design mais acurado é tipicamente deixado para os programadores, não para o designer. Então como traduzir “eu quero que esse cara ande 3 pixels e pule quando chegar perto de uma parede e fique lá, numa pose bacana se ele conseguir ou fique triste quando aterrissar” para um programador bitolado cujos olhos estão velados pelas intrincações da programação?

Máquina de estados Finitos.

Quer dizer, você diz ao programador exatamente o que deve acontecer enquanto em cada estado de uma ação geral (normalmente arbritária) e explica exatamente como cada estado deve se relacionar com o outro. Em suma, sem um pseudocódigo desse tipo, a explicação cai em ouvidos moucos.
Na verdade, com o game maker, nós somos normalmente o designer e o programador ao mesmo tempo. Entretanto, nós ainda temos que fazer nossos jogos sob o ponto de vista do designer, quer nós saibamos como programar tudo ou não. E essa é a beleza das MEF. Elas permitem a um artista fazer o trabalho de um programador com pouco a nenhum conhecimento de programação. Entretanto, com Game Meker, nós simplesmente colocamos um pouco de código num estado que serve de parte de uma máquina de estados finitos e isso funciona! Porque o Game Maker faz o trabalho de programação por nós e nós damos a ele o pseudocódigo (mais ou menos) com GML para fazê-lo entender o que nós queremos.
A diferença entre usar a Máquina de estados finitos e usar o velho método com declarações “IF” surge quando você percebe que se tornou o “programador” ao invés do designer em algum ponto. E, quando você tenta voltar a ser o designer, fica muito difícil devido às diferenças nos pensamentos envolvidos. O método do designer é criativo, enquanto o método do programador é técnico e eficiente. Você acaba digitando, linha por linha, um design que quer criar. Mas, por mais que criatividade seja ótima, raramente é eficiente. Enquanto o método “linha-por-linha” possa ser criativo a princípio e te permite ver resultados imediatos, ele não é eficiente. A falta de eficiência cresce exponencialmente a cada linha que você acrescenta, eventualmente levando a códigos misteriosos com bugs invisíveis (de tão complexos que eles ficam) e tornando-se tão ineficiente ao rodar se você continuar programando assim que acabará tirando sua possibilidade até de CRIAR. Você termina tentando ser “programador” e “designer” ao mesmo tempo e acaba falhando em ambos simplesmente porque não usou a vantagens de nunehum. Aí, você experimenta aquela (tão) familiar experiência de jogo abandonado e eu, pelo menos, estou REALMENTE cansado de ver isso. Tantos projetos fantásticos indo pro lixo por causa disso! Estou aqui para fazer o meu melhor para prevenir que isso aconteça contigo!
Se levar esse artigo a sério, você não irá querer de programar seus jogos. Afinal, o foco é fazer jogos com facilidade! E, em Game Maker, isso não é difícil de jeito nenhum! Tudo que você precisa é de um bom e funcional método de programação. E isso, meus bons leitores, é o que vou ensinar agora mesmo! Continuemos com o show!

Vamos falar sobre estados:

Contrariamente ao conhecimento popular, um estado é simplesmente “como” um objeto ou entidade existe. Quer dizer, simplesmente mudar uma variável para estado=”algo” e checar isso com uma declaração “if” NÃO é como um estado funciona.
Infelizmente, jogos ainda são programados dessa forma com Game Maker. Sempre foram e provavelmente sempre serão. Infelizmente, isso deixa seu jogo MUITO MUITO complicado quando você tem muitos tipos de estado e muitos tipos de física que podem mudar ou não mudar dependendo de como está aquilo que você chamou de “estado” do seu objeto. Por isso as pessoas, às vezes, precisam de muitos objetos para fazer coisas complexas quando essas ações poderiam ser feitas com muito mais facilidade (e eficiência!) com um único objeto numa máquina de estados.
Como expliquei antes, um estado é “como” um objeto existe. Quer dizer, ele normalmente tem um certo sprite e um certo tipo de física apicado a ele enquanto está naquele estado. Entretanto, um objeto típico não existe sempre num estado. É aí que a “máquina de estados finitos” vem a calhar. Ela permite que um objeto transite entre esses estados. Em suma, ela permite que um objeto exista de várias formas diferentes, mas de uma só forma de cada vez. Cada estado pode transitar por vários outros estados ou apenas para um outro e então permanecer assim, pelo tempo que quiser, existindo assim das formas que quiser. Isso permite um fluxo orgânico para a programação do objeto que o Game Maker, por alguma razão, simplesmente não traz consigo.

Um exemplo do OUTRO método:

Irei programar, agora, um objeto que tem alguns mutuamente excludentes falsos “estados”, como os chamarei de agora pra frente. Seu comportamento é ficar parado por 10 steps, mostrando um sprite de espera, depois mostra um sprite de preparação por mais 5 steps, então anda 3 pixels enquanto mostra um sprite de caminhar e, se ele bate numa parede, mostra um sprite de pancada e volta 3 pixels, caso contrário, fica numa posição de vitória por mais 10 steps e faz tudo de novo. Pra evitar o uso de alarmes, farei tudo no evento step e assumirei que todas as variáveis usadas foram inicializadas no create event (mais trabalho, por sinal). “Ah, mas isso é mole”, você diz? Bem, vejamos quão fácil é... (e, cá pra nós, se você é novo em GML, sinta-se livre pra ignorar esse código, apenas repare no tamanho e complexidade).
Código:

if estado=="parado" and espera!=10 {
          espera += 1
          sprite_index = spr_parado
          }
        if estado=="parado" and espera==10 {
          sprite_index = spr_preparado
          estado="preparado"
          espera = 0
          }
        if estado=="preparado" and espera<5 {
          espera += 1
          }
        if estado="preparado" and espera==5 {
          sprite_index = spr_caminhando
          estado = "caminhando"
          espera = 0
          }
        if estado=="caminhando" and espera< 3 and place_meeting(x,y,obj_parede)=false {
          x += 1
          }
        if estado=="caminhando" and espera< 3 and place_meeting(x,y,obj_parede)=true {
          sprite_index = spr_pancada
          estado = "batido"
          espera = 0
          }
        if estado=="batido" and espera<3 {
          x -= 1
          espera += 1
          }
        if estado=="batido" and espera==3 {
          sprite_index = spr_parado
          estado = "parado"
          espera = 0
          }
        if estado=="caminhando" and espera==3 {
          sprite_index = spr_vitoria
          estado = "vitoria"
          espera = 0
          }
        if estado=="vitoria" and espera<10 {
          espera += 1
          }
        if estado=="vitoria" and espera==10 {
          sprite_index = spr_parado
          estado = "parado"
          espera = 0
          }

Nossa, que trem feio! Imagine se eu fosse incluir pro cara OUTROS comportamentos, como caminhar e pular do modo normal, ou ser acertado por um inimigo? Tudo o que queríamos era fazer o sujeito caminhar um pouco e rebater se desse de cara com uma parede ou ficar numa pose vitoriosa se não batesse. Aposto que você pensou que “fácil desenvolvimento de jogos” fosse a filosofia de nosso amado game maker. Por que diabos uma coisinha dessas ficou tão difícil?? Imagine só, programar um moderno jogo de lutas desse jeito!
Bem, estou aqui pra te dizer que não é o game maker que é muito difícil, mas você simplesmente não está usando ele com todas as suas capacidades, ainda. Na verdade, programar algo assim pode facilmente ser MUITO MENOS demorado e, por conseqüência, muito menos susceptível a erros, quando usada uma simples, mas infinitamente útil, técnica conhecida como “máquina de estados finitos”.
Como efeito colateral, seu código pode até se tornar algo que bons programadores chamam de “modular”.
O que é essa criatura, “modular”, você pergunta? Bem, é simplesmente dizer que, diferente do código anterior, nada é duramente codificado(1). Seu código pode ser reusado em outros lugares. Programe o comportamento de um inimigo através de estados, por exemplo, e simplesmente adicione sprites, entrada de controles ou entrada de IA num estado de inicialização a seu gosto para permitir controlar o comportamento e simplesmente inicializar os sprites usados numa base de objeto-por-jogo, talvez no create event.
Criar um jogo complexo não tem que ser difícil! Basta você pensar em termos modulares. Ou seja, em nosso caso, cada estado (ou parte de um estado) deve ser observado como um comportamento e, como tal, esses podem ser facilmente reutilizados. Mas eu provavelmente tenho que te ensinar, primeiro, como programar esses comportamentos, certo? Então vamos lá!

USANDO a máquina de estados finitos:

Ok, agora a diversão! Vou te mostrar algo MUITO mais fácil! Entretanto, antes de CRIAR nossos comportamentos, precisamos aprender a USÁ-LOS. Entretanto, para usá-los, você precisa de um script para a “máquina de estados finitos”. Ele entra no step event com outro índice de script passado como argumento que será nosso primeiro estado. Ele tem a forma:
Código:
st_maquina(st_default)
Esse script faz uma coisa e apenas essa coisa: ele transita entre estados. Sem esse script, seria impossível mudar de estados ou mesmo permanecer em algum. O argumento passado é realmente só o nome do script, sem a parte “()”. Esse script, st_default, é onde você começa a programar uma máquina default. Não importa o nome dele, eu só uso “st_” para preceder meus scripts, para saber o que é (e o que não é) parte de uma máquina de estados.
Primeiramente, antes de intrar na programação de nossos estados, você precisará saber alguns comandos básicos e variáveis locais para poder usar o script st_maquina. Eles devem ser definidos como constantes, se você tiver problemas em usá-los em conjunto com as variáveis. Eu mostro depois, pra evitar confusão.
Vamos começar com os comandos:

estado_continua
estado_repete
estado_proximo
estado_proximo_agora


Eles têm a forma “return(tipo_de_transição)” onde tipo_de_transição é um dos quatro tipos de transição entre estados. Eles são todos que você vai precisar, não importa o que faça num estado. O terceiro é um pouco especial, então voltaremos a ele num minuto.
estado_continua simplesmente continua num estado em que já estamos, no próximo step (ele roda o código do script atual de novo, mas permite ao game maker continuar fazendo o resto das coisas que ele tem que fazer nesse step). Por outro lado, estado_repete vai continuar repetindo esse estado constantemente até que se mande parar, efetivamente fazendo dele o equivalente ao loop “while”. Durante esse tempo, nada mais acontece fora do script, então seu jogo pode congelar se você não sair em algum ponto.
estado_proximo e estado_proximo_agora são um pouco diferentes dos outros dois porque eles requerem a declaração de uma variável antes que eles possam ser chamados. Essa variável, embora possa parecer confusa a princípio, simplesmente recebe o índice do script a ser chamado para ser o próximo estado e, portanto, será chamada de “proximo_estado”.
Quando transitando de um estado para outro, você simplesmente escreve:
Código:

proximo_estado=st_qualquer_coisa
return(proximo_estado)
Tem que ser nessa ordem porque o código para de ser executado assim que return() aparece, como se fosse uma declaração exit.
Finalmente vou mostrar as variáveis. Diferente das constantes que retornamos, essas podem ser mudadas.

tempo_no_estado
proximo_estado
indice_do_estado


A primeira é incrivelmente útil. Ela cresce em uma unidade a cada step, enquanto você permanecer nele, mas se você mudar de estado, ela magicamente se reseta também. Porém, se você quiser algo ocorrendo em intervalos, você pode fazer acontecer e resetar a variável manualmente toda vez que ela chegar num determinado valor. Isso não terá impacto algum no funcionamento da maquina de estados, então use e abuse.
Como já foi explicado o que “proximo_estado” faz, só queria lembrar de criar uma constante a ser usada nos valores de transição se você tender a confundir proximo_estado e “estado_proximo”. Isso facilitará algumas coisas.

Finalmente, indice_do_estado é uma especial. Eu deixei pro final porque quero que você esqueça que ela existe. Na verdade, ela é bem útil... nas mãos de quem sabe o que está fazendo.
Essa é uma forma alternativa de programar transições entre estados. Você pode mudar um estado diretamente usando essa variável. Pode ser útil se você quiser sair de um estado instantaneamente se o usuário aperta uma tecla, por exemplo, e você quer usar os eventos do game maker. De outra forma, você simplesmente declarará “proximo_estado” naquele evento-chave e usar um script que roda algum código e usa “return(proximo_estado)” a cada step se você ainda quiser usar transições e os eventos de entrada do game maker. Entretanto, tenho certeza que será mais fácil usar os códigos de entrada universais enquanto dentro de um estado de modo a checar por todos pressionamentos de botão a menos que você queira usar códigos complicados(2) para determinar que botões um jogador pode usar (o que, na minha opinião, não é uma boa idéia).
De qualquer forma, se você não entendeu o último parágrafo, sinta-se livre para relê-lo até entender ou esquecer que essa última variável sequer existe e se livrar de algumas dores de cabeça. Ela só existe para pessoas que querem muita flexibilidade de programação, como eu.
Por último, tem os argumentos. É sempre útil usar argumentos entre os estados, porém eu não faço muito uso deles, pessoalmente. Se for necessário, eles estão definitivamente lá. Apresentam-se da forma:
arg0
arg1
arg2

Essas são variáveis que podem ser declaradas e des-declaradas à vontade. Eu não as retorno a zero, então você terá que fazer isso manualmente após passá-las a outro script. Apenas garanta que você as ajeitou antes de passá-las. De outra forma, elas sempre serão ou zero ou o valor que você as declarou.

O método “máquina de estados finitos”:

Finalmente, vamos mostrar o que aprendemos a fazer algumas transições. POR FAVOR, seja mais fácil que da última vez! Eu não quero programar daquele jeito NUNCA MAIS.
Relembrando: o comportamento era ficar num estado de espera por 10 steps, mostrando um sprite “parado”, ficar mais 5 steps mostrando um sprite “preparado”, depois mover 3 pixels enquanto mostra um sprite caminhando e então, se bater numa parede, mostrar um sprite de pancada e voltar 3 pixels e, se não bater, ficar numa pose de vitória por 10 steps e começar de novo.
Precisaremos de apenas 4 estados: st_parado, st_caminhando, st_batido e st_vitoria. Então vamos precisar de 4 scripts. Depois, definimos nosso estado default no script st_maquina como st_parado.
No primeiro script, st_parado, colocamos esse código:
Código:

if (tempo_no_estado == 0 && sprite_index!=spr_preparado)
 sprite_index = spr_parado; //define nosso sprite parado

if (tempo_no_estado = 10 and sprite_index!=spr_preparado) {
 sprite_index = spr_preparado; //define nosso sprite preparado
 tempo_no_estado = 0;
 }

if (tempo_no_estado = 5 && sprite_index==spr_preparado) {
 proximo_estado = st_caminhando;
 return(estado_proximo); //abandona o atual estado, indo para st_caminhando
 }
return(estado_continua); //se não aconteceu nada, permanecer aqui
Vamos ao próximo estado (que existe apenas para demonstração). Esse comportamento pode ser abreviado, mas usar estados separados pode manter as coisas muito organizadas quando você precisar.
Código:

if (tempo_no_estado = 0) then sprite_index = spr_caminhando;
        if (place_meeting(x+1,y,obj_parede)=true) {
          proximo_estado = st_batido; //se batemos em algo,
          return(estado_proximo_agora); //nós abandonamos esse estado
          }
        if (tempo_no_estado < 3) then x+=1; //se ainda estamos nesse estado, vamos em frente e nos movemos
        if (tempo_no_estado = 3) { //porém, se terminamos de nos mover, vencemos
          proximo_estado = st_vitoria;
          return(estado_proximo)
          }
        //mas, se ainda estivermos caminhando, continuamos tentando caminhar no próximo passo
        return(estado_continua);
Agora, o st_pancada...
Código:

 x -= 1;
        if (tempo_no_estado < 3) then sprite_index = spr_pancada //sprite de levar a pancada
        else {
          proximo_estado = st_parado; //ou de ficar parado, se já voltamos o suficiente
          return(estado_proximo);
        }
        return(estado_continua)  //senão, simplesmente ficamos aqui por mais alguns steps.
...e st_vitória:
Código:

sprite_index = spr_vitoria; //muda o sprite para esse estado
        if (tempo_no_estado==10) { //se já é hora de parar de fazer pose,
          proximo_estado = st_parado;
          return(estado_proximo_agora); //nós voltamos ao estado de ficar parado NESSE step
          }
        return(estado_continua); //se ainda estamos posando, continuamos com a pose. :)

E está pronto! O sujeito vai ficar parado um pouco, entrar num estado de preparação, andar e, se bater numa parede, vai retroceder um pouco, senão, vai fazer uma pose de vitória e começar tudo de novo.

Comentários finais:

Game Maker é uma ferramenta espetacular e parece ter sido desenvolvida com suporte à Máquina de Estados Finitos, embora nunca realmente diga isso. Eu peço urgência para que você aprenda e use bem essa ferramenta, porque, quer você acredite ou não, é o método mais eficiente. Afinal, você sempre pode programar um estado de inicialização de sprite e simplesmente exportar seus scripts de comportamento, permitindo que eles sejam partilhados com outras pessoas de forma que eles os usem em seus jogos! O melhor de tudo é que qualquer outro personagem, inimigo ou aliado, poderia usar a mesma máquina de estados para preparar seus sprites, mas comportar-se diferentemente usando um sistema de transição via entrada de dados, seja por botões ou por alguma IA complicada que emule esses botões.
Por mais estranhos e inúteis que esses estados sejam, meu exemplo ilustra quão rápida e facilmente uma complicada série de ações poderia ser facilitada com uma máquina de estados finitos sem um monte de declarações “IF”. É muito simples programar desse jeito e isso faz programar personagens complicados uma baba.

    (1) hard-coded. Não consegui pensar em tradução adequada.
    (2) Mais uma vez, hard-code


Aqui eu termino a tradução. Ainda havia mais um parágrafo com link para uma engine que usa essa state machine. No exemplo, havia um jogo do mario, do megaman e outro que não me lembro. Não vou postar re-fazer o exemplo porque é muita coisa pra traduzir. No lugar disso, deixo (acima) o link para o post original, caso você tenha interesse.

Agora, o script que faz tudo o que foi dito acima possível!!!

Código:
//maquina_de_estados(st_default);

//Chame esse script no evento step para usar a máquina de estados finitos

//[Introdução]
// Máquinas de estado finite são um aforma simples de programar objetos porque
// cada estado é, em essência, um momento no tempo para o objeto. É COMO
// o objeto existe, por assim dizer, (ou se comporta) naquele momento, enquanto em um estado.
// Já que pode haver apenas um estado por vez, cada estado é independente dos outros,
// então apenas a transição entre eles importa. Isso dá uma imensa flexibilidade na
// programação de comportamentos dos objetos, simplesmente porque, não importa
// como eles se comportem, a máquina de estados flui.
// Contanto que você controle esse fluxo, não importa o que ele contenha. Você só precisa
// saber o que está acontecendo agora e onde ele estará quando acabar (se é que estará em
// algum lugar).
 
//[Notas]
// Um st_script deve detalhar como um objeto está se comportando em um dado ponto do tempo,
// então programe livremente enquanto em um estado. Use input_check() para checar
// a quanto tempo os botões estão sendo apertados durante um estado para saber se o
// estado deve se alterar. Além disso, use a variável “tempo_no_estado” para checar o comprimento
// de tempo (o número de steps) que o objeto está naquele estado.

// Use "proximo_estado = st_script" para indicar qual é o próximo estado. SEMPRE
// retorne (return()) um valor para indicar COMO será a transição para o outro estado (ou se
// quiser apenas repetir o atual). Mude de estado retornando “estado_proximo”
// OU, para entrar num estado no mesmo step, retorne “estado_proximo_agora”
// As ações retornadas e args são o coração de qualquer máquina de estados finitos,
// então use e abuse!!!


//Inicialização
if variable_local_exists("indice_do_estado")==0 {
indice_do_estado = argument0 //estado default state a ser executado (NÃO mude isso diretamente!)
proximo_estado = argument0 //próximo estado a ser executado (use ISSO para mudar estados) tempo_no_estado = 0 //tempo em que estivemos no estado atual
//Argumentos
if variable_local_exists('arg0')=0 arg0 = 0 //Use essas variáveis se quiser passar argumentos para os scritps
if variable_local_exists('arg1')=0 arg1 = 0 //Elas serão limpas apenas quando você mudar para um novo estado
if variable_local_exists('arg2')=0 arg2 = 0
if variable_local_exists('arg3')=0 arg3 = 0
if variable_local_exists('arg4')=0 arg4 = 0
if variable_local_exists('arg5')=0 arg5 = 0
if variable_local_exists('arg6')=0 arg6 = 0
if variable_local_exists('arg7')=0 arg7 = 0
limpa_argumentos = 0 //Mude essa variável para 1 antes de mudar de estado para limpar os args para 0

//Retorna ações (Essas variáveis já são consideradas como definidas como sonstantes nessa máquina)
// estado_proximo_agora = 3 //executa o próximo estado nesse step
// estado_proximo    = 0 //execute o próximo estado no próximo step
// estado_continua = 1 //continues nesse estado no próximoa step
// estado_repete  = 2 //repete esse estado AINDA NESSE step
}

//Vamos limpar os argumentos
if limpa_argumentos = 1 {
  arg0 = 0
  arg1 = 0
  arg2 = 0
  arg3 = 0
  arg4 = 0
  arg5 = 0
  arg6 = 0
  arg7 = 0
  limpa_argumentos = 0
  }

//Processa  Transições entre Estados
var prxm;
prxm = script_execute(indice_do_estado,arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7)

while(prxm == estado_repete) { //repete estado mas não vai para o próximo step
  //Vamos limpar os argumentos, se necessário
  if limpa_argumentos = 1 {
    arg0 = 0
    arg1 = 0
    arg2 = 0
    arg3 = 0
    arg4 = 0
    arg5 = 0
    arg6 = 0
    arg7 = 0
    limpa_argumentos = 0
    }
  prxm = script_execute(indice_do_estado,arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7);
  tempo_no_estado += 1;
  }

while(prxm == estado_proximo_agora) { //vai para o próximo estado nesse step
  indice_do_estado = proximo_estado;
  tempo_no_estado = 0;
  prxm = script_execute(indice_do_estado,arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7)
  if prxm = estado_repete
  {script_execute(maquina_de_estados); exit;}
  //Vamos limpar os argumentos, se necessário
  if limpa_argumentos = 1 {
    arg0 = 0
    arg1 = 0
    arg2 = 0
    arg3 = 0
    arg4 = 0
    arg5 = 0
    arg6 = 0
    arg7 = 0
    limpa_argumentos = 0
    }
  }

if(prxm == estado_proximo) { //muda de estado no próximo step
  indice_do_estado = proximo_estado;
  tempo_no_estado = 0;
  //Vamos limpar os argumentos, se necessário
  if limpa_argumentos = 1 {
    arg0 = 0
    arg1 = 0
    arg2 = 0
    arg3 = 0
    arg4 = 0
    arg5 = 0
    arg6 = 0
    arg7 = 0
    limpa_argumentos = 0
    }
  }

if(prxm == estado_continua) { //Continua nesse estado no próximo step
  tempo_no_estado += 1;
  }

Criei um exemplo que mostra algumas transições (acabei não pensando numa situação para usar o estado_repete). Por favor, ignorem os possíveis erros de programação e os gráficos, uns "roubados", outros terríveis, e concentrem-se em entender como a máquina funciona.
Eu poderia ter usado menos estados e menos variáveis, bem como códigos mais simples, mas queria mostrar justamente como a máquina torna possível fazer muita coisa, digitar muita coisa, sem enlouquecer.

Aqui o link pra download


Última edição por Pedro Henrique em Dom 03 Jul 2011, 13:10, editado 6 vez(es) (Razão : acréscimo de exemplo)

saim

Ranking : Nota B
Número de Mensagens : 2964
Idade : 38
Data de inscrição : 14/01/2011
Notas recebidas : C-D-A-B
Reputação : 121
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 1
   : 0
   : 3

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por vinians em Seg 21 Fev 2011, 11:30

Otimo tutorial amigo, uso muito essas máquinas de estado em meus games, inclusive estou usando uma agora no jogo do campeonato. Vai ajudar muita gente que não conhece.

vinians

Ranking : Nota B
Número de Mensagens : 2490
Idade : 27
Data de inscrição : 18/09/2008
Notas recebidas : B-C-A-C
Reputação : 52
Insignia 1 x 0 Insignia 2 x 1 Insignia 3 x 0
Prêmios
   : 0
   : 2
   : 1

http://xcreatorgames.weebly.com/

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por dharrison em Seg 21 Fev 2011, 11:59

mto legal, eu não conhecia ainda esses métodos
vai ajudar muito, mas tbm parece ser bem complicado (a principio)

vou baixar as engines, mas se vc puder fazer um mini tudo ensinando como usar e criar os estados passo a passo (só um aprofundamento) seria bom tbm

abração cara
parabens pelo tuto
esse merece ser Fixo xD

dharrison

Ranking : Nota B
Número de Mensagens : 1243
Idade : 25
Data de inscrição : 04/02/2009
Notas recebidas : A - B
Reputação : 89
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 1
Prêmios
   : 0
   : 1
   : 0

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por saim em Qua 23 Fev 2011, 09:38

dharrison, vou tentar dar uma finalizada num jogo que uso a máquina pra fazer uma IA pra lá de complexa (mas que abandonei porque estava complexa demais e encheu :oops: ), mas não tem mistério não.
Você cria um script que vale pra um step. Esse script pode ser repetido indefinidamente (usando estado_continua) ou, dependendo da situação, chamar outro script (definindo proximo_estado e usando estado_proximo).
Isso te leva a evitar (mas não te proíbe) o uso de algumas built-in's, como speed, por exemplo, porque o próximo estado pode exigir que o objeto esteja parado.

O truque é definir exatamente o que você quer que aconteça (atuando como designer) ANTES de começar a programar (atuando como programador). Esse foi o pecado que me levou a abandonar meu jogo, eu fui tendo idéias à medida que programava, o que me levava a re-desenhar todos os... sei lá... 20 scripts várias e várias vezes.

saim

Ranking : Nota B
Número de Mensagens : 2964
Idade : 38
Data de inscrição : 14/01/2011
Notas recebidas : C-D-A-B
Reputação : 121
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 1
   : 0
   : 3

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por Markituh em Ter 01 Mar 2011, 08:08

Opa saim, ótimo sistema! Estou tentando fazê-lo aqui no meu PC, mas estou com certos problemas Happy" Você comentou no script para usar input_check, o que seria exatamente essa função? Não há como fazer por Keyboard Check ou Keyboard Press? E outra coisa, qual é o conteúdo do script st_default? Happy"

___________

"Não deixe para amanhã o que se pode fazer hoje"

Links úteis:
Índice de Tutoriais
Manual online do GMS

Markituh

Ranking : Sem avaliações
Número de Mensagens : 2183
Data de inscrição : 11/10/2009
Reputação : 106
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por saim em Ter 01 Mar 2011, 09:02

Markituh, vou te confessar que não faço idéia do que é essa função. Eu realmente só traduzi o que vi na outra comunidade.
Me parece que é exatamente o que você sugeriu, acrescentando os mouse_checks e os inputs do joystick.

Eu ainda estou devendo o exemplo, né? Agora devo ter mais tempo, o exemplo não deve demorar muito.

saim

Ranking : Nota B
Número de Mensagens : 2964
Idade : 38
Data de inscrição : 14/01/2011
Notas recebidas : C-D-A-B
Reputação : 121
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 1
   : 0
   : 3

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por Markituh em Ter 01 Mar 2011, 09:11

Ah, firmeza. Tá dando altos erros com Unknow variable aqui, no meio dessa bagaça toda não sei diferenciar os scripts de constantes scratch Talvez com o seu exemplo fique melhor a compreensão. E... Não me recordo de ter sugerido algo ._. Só perguntei se não podia usar o evento Keyboard Check ou Keyboard Press, tinha que ser esse aí. Input_check deve ser algum script postado na GMC, é bom dar uma procurada e mostrar pro pessoal.

Aguardando o exemplo, planejo usar a MEF para fazer um jogo super ultra incrível, que ganhará nota A e talz Cool

___________

"Não deixe para amanhã o que se pode fazer hoje"

Links úteis:
Índice de Tutoriais
Manual online do GMS

Markituh

Ranking : Sem avaliações
Número de Mensagens : 2183
Data de inscrição : 11/10/2009
Reputação : 106
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por saim em Ter 01 Mar 2011, 10:24

As variáveis que provavelmente deram como "unknown" são aquelas de transição: estado_proximo_agora, estado_proximo , estado_continua e estado_repete (que acabo de perceber que não traduzi os comentários...). Se forem essas, você pode escolher declará-las como variáveis locais no create do objeto que usa a MEF ou declará-las como globais no global game settings, usando os valores sugeridos (0, 1, 2, 3) ou o que você preferir. Eu levei algum tempo pra entender como elas funcionam, então suponho que outras pessoas também possam ter dificuldades nesse aspecto.

A tradução de "input_check" é "checagem de entrada". As entradas são exatamente os key_checks, button_checks, etc, por isso acho que trata-se realmente do que você mencionou (não sugeriu ^_^ ). Ou seja, input_check seria um termo que abrange todas as formas de checar entradas do usuário.
Sem chance de ser algum script da GMC. Se fosse, o script obrigatoriamente estaria incluído no exemplo, o que não é o caso.

Particularmente, eu uso a MEF só pra quando o objeto é mais complexo, ou seja, ele requer mudança de estados pra ser programado. Aqueles inimigos que andam de um lado pra outro dispensam a ferramenta, enquanto um player, que pula, anda, corre, come cogumelos, ganha poderes temporários, etc, pode ser programado com muito mais facilidade.
Ela tem uma desvantagem que não mencionei: ela usa scripts. Qual o problema nisso? Scripts declaram 8 variáveis a cada vez que são chamados (mesmo que você não use todos os argumentos, eles SÃO declarados como "0"). Chamar muitos scripts em cada step pode deixar o jogo lento. Um único objeto dificilmente causará problemas, mas se você fizer um jogo com ninjas inimigos super-astutos, faça poucos ninjas de cada vez.

saim

Ranking : Nota B
Número de Mensagens : 2964
Idade : 38
Data de inscrição : 14/01/2011
Notas recebidas : C-D-A-B
Reputação : 121
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 1
   : 0
   : 3

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por MatheusReis em Ter 01 Mar 2011, 10:44

Muito bom mesmo.
Eu uso em todos os meus jogos, é minha parte favorita do grande sistema de engrenagens do Game Maker GM8 Happy
Parabens pelo Tuto

MatheusReis

Ranking : Nota A
Número de Mensagens : 1087
Idade : 23
Data de inscrição : 13/01/2010
Notas recebidas : B-A-A
Reputação : 30
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 2
   : 1
   : 0

http://www.mathaeuz.deviantart.com

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por Kabeção em Qua 02 Mar 2011, 17:16

Assim como programação modular é programar as partes de um programa de forma que funcionem separadamente (como a máquina de estados) hard-coded é o oposto onde tudo é misturado e é difícil de se codificar, ou seja, interpretar.

Eu sempre uso constantes para os nomes dos estados, é uma forma mais otimizada:
Código:
// Constantes
parar = 0;
correr = 1;
pular = 2;
Assim posso mudar os estados facilmente na hora de testes por exemplo apenas acrescentando +1 ou impedir que outros estados que não existam sejam definidos apenas fazendo isso:
Código:
estado = estado mod ultimo_estado+1;
Usar strings abre muito espaço para erros.

O mesmo autor desse tutorial tem uma engine muita boa sobre movimentação irregular.
É bem mais extensa mas é algo bem útil de se aprender como as máquinas de estado.

Kabeção

Ranking : Sem avaliações
Número de Mensagens : 2314
Data de inscrição : 08/06/2008
Reputação : 100
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 3
   : 0
   : 1

http://blackcapapps.blogspot.com.br/

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por saim em Qua 02 Mar 2011, 22:27

Adicionado um exemplozinho comentado no final do tópico original.

saim

Ranking : Nota B
Número de Mensagens : 2964
Idade : 38
Data de inscrição : 14/01/2011
Notas recebidas : C-D-A-B
Reputação : 121
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 1
   : 0
   : 3

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por Markituh em Qua 02 Mar 2011, 23:11

Opa, então o st_default definia as variáveis, certo... Tentei achar no st_pula_sobe alguma forma de limitar o quanto o player pula, mas isso não é importante. O ruim é que constantes só são permitidas na versão Pro, que pena, apesar de eu ter achado uma forma de definir constantes com a Lite Mas assim, há pouca chance dessa forma funcionar, seria defini-las num editor hexadecimal, mas para isso teria que saber o ponto EXATO para inserir as constantes, sem falar no cuidado total com as endstrings. Saim, teria como explicar uma maneira de fazer isso usando variáveis comuns? Pretendo programar um projeto com esse sistema, e como também pretendo postar na Yoyogames, terei sérios problemas se estiver usando um crack.

___________

"Não deixe para amanhã o que se pode fazer hoje"

Links úteis:
Índice de Tutoriais
Manual online do GMS

Markituh

Ranking : Sem avaliações
Número de Mensagens : 2183
Data de inscrição : 11/10/2009
Reputação : 106
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por saim em Qua 02 Mar 2011, 23:45

Markituh, o que eu fiz com pro foi definir as constantes estado_proximo_agora, estado_proximo , estado_continua e estado_repete como "user-defined constants". Pra contornar isso, você pode fazê-las globais através de algum outro objeto ou mesmo declará-las no create do obj_player.
Não entendi sua idéia, mas me parece que essas opções que descrevi são mais simples (não sei porque não fiz o exemplo assim, vou mudar e atualizar)
Edit: Pronto. Agora, a menos que haja alguma função pro durante os estados (acho que não tem), o exemplo roda em lite.

Pra limitar o quanto o player pula, você pode definir algum argumento (arg0-7) como "y-altura" logo antes de passar para o "st_pula_sobe" e proibir o "y" de ser menor que o argumento. Só que, aí, seria bom de lembrar de limpar os agumentos SEMPRE que sair desse estado. Outra forma visualmente mais agradável seria calcular a velocidade inicial, a gravidade e o tempo de "pulo longo" de modo a atingir o teto desejado.

saim

Ranking : Nota B
Número de Mensagens : 2964
Idade : 38
Data de inscrição : 14/01/2011
Notas recebidas : C-D-A-B
Reputação : 121
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 1
   : 0
   : 3

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por Markituh em Qui 03 Mar 2011, 06:56

Bom, as constantes funcionam, mas não tem como definí-las na Lite. Uma forma seria eu fazer meu projeto a partir do seu exemplo, já que as constantes estão declaradas no arquivo .gmk, mas não seria tão legal... Vou passar um tempo estudando esse sistema, quem sabe meu jogo não fica legal? Gostaria de te perguntar algo, irei mandar uma MP.

___________

"Não deixe para amanhã o que se pode fazer hoje"

Links úteis:
Índice de Tutoriais
Manual online do GMS

Markituh

Ranking : Sem avaliações
Número de Mensagens : 2183
Data de inscrição : 11/10/2009
Reputação : 106
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por saim em Qui 03 Mar 2011, 10:25

Kabeção escreveu:Eu sempre uso constantes para os nomes dos estados, é uma forma mais otimizada:
Código:
// Constantes
parar = 0;
correr = 1;
pular = 2;
Assim posso mudar os estados facilmente na hora de testes por exemplo apenas acrescentando +1 ou impedir que outros estados que não existam sejam definidos apenas fazendo isso:
Código:
estado = estado mod ultimo_estado+1;
Usar strings abre muito espaço para erros.

Essa máquina apresentada aqui também não usa strings, mas scripts, que já são armazenados como variáveis por natureza, então eu acabo fazendo (mais ou menos) a mesma coisa. Só que, com essa máquina, não é recomendável mudar os estados dessa forma, por vários motivos:
- se você eliminou algum script durante a programação, o valor das variáveis não será sequencial, causando erros.
- mudar o estado manualmente requer conhecimento da máquina a fim de evitar problemas com os argumentos (arg0-7) e o tempo_no_estado.
- você perde a flexibilidade que as mudanças aqui apresentadas oferecem (estado_repete, estado_proximo_agora)
Fora isso, o fato dos scripts serem variáveis pode causar um erro um pouco mais grave se você definir ainda outros valores a eles. Por exemplo, suponha que o jogo declare (automaticamente) que o estado st_default vale 1. Depois, ao atribuir novos valores aos estados, você declare (manualmenmte) que é o st_atirando que vale 1. Ao chamar o estado que vale "1", qual dos dois será chamado? Sinceramente, não sei.

saim

Ranking : Nota B
Número de Mensagens : 2964
Idade : 38
Data de inscrição : 14/01/2011
Notas recebidas : C-D-A-B
Reputação : 121
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 1
   : 0
   : 3

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por Kabeção em Qui 03 Mar 2011, 17:57

saim escreveu:
Kabeção escreveu:Eu sempre uso constantes para os nomes dos estados, é uma forma mais otimizada:
Código:
// Constantes
parar = 0;
correr = 1;
pular = 2;
Assim posso mudar os estados facilmente na hora de testes por exemplo apenas acrescentando +1 ou impedir que outros estados que não existam sejam definidos apenas fazendo isso:
Código:
estado = estado mod ultimo_estado+1;
Usar strings abre muito espaço para erros.

Essa máquina apresentada aqui também não usa strings, mas scripts, que já são armazenados como variáveis por natureza, então eu acabo fazendo (mais ou menos) a mesma coisa. Só que, com essa máquina, não é recomendável mudar os estados dessa forma, por vários motivos:
- se você eliminou algum script durante a programação, o valor das variáveis não será sequencial, causando erros.
- mudar o estado manualmente requer conhecimento da máquina a fim de evitar problemas com os argumentos (arg0-7) e o tempo_no_estado.
- você perde a flexibilidade que as mudanças aqui apresentadas oferecem (estado_repete, estado_proximo_agora)
Fora isso, o fato dos scripts serem variáveis pode causar um erro um pouco mais grave se você definir ainda outros valores a eles. Por exemplo, suponha que o jogo declare (automaticamente) que o estado st_default vale 1. Depois, ao atribuir novos valores aos estados, você declare (manualmenmte) que é o st_atirando que vale 1. Ao chamar o estado que vale "1", qual dos dois será chamado? Sinceramente, não sei.
Entendo, está usando diretamente o id dos scripts para identifica-los e não seu nome propriamente dito.
Mas para tudo isso eu costumava usar outro método como ds_grid que facilitava na hora de criar um padrão sendo que cada estado tinha os valores certos e assim usar apenas um script para executa-los ou simplesmente variáveis também mas não concentradas em scripts.

Parando para pensar agora, usar um script para cada estado é realmente o melhor método e o mais funcional já que eles não só definem variáveis mas também, como você disse, definem muitos outras coisas como o próximo estado, já executam as ações (como mover, lançar magia e etc) e ainda da para tratar imprevistos como quebra da ação, mudança repentina e coisa e tal.

Hehe, acabei de aprender um novo conceito para melhorar o sistema de meus projetos!
Não tinha percebido isso quando li o original, vlw! Happy

Kabeção

Ranking : Sem avaliações
Número de Mensagens : 2314
Data de inscrição : 08/06/2008
Reputação : 100
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 3
   : 0
   : 1

http://blackcapapps.blogspot.com.br/

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por DS Santos em Seg 07 Mar 2011, 21:08

Praticamente obrigatorio o uso disso em jogos de luta.
O controle sobre as ação é muito otimizado mas a programação, no inicio do projeto, é bem extensa e não é algo que pode ser usado por qualquer um sem experiencia.

DS Santos

Número de Mensagens : 98
Data de inscrição : 19/05/2010
Reputação : 2
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por anth201 em Qua 22 Maio 2013, 21:05

Muito bom Mas tem como fazer cenarios aleatorios?

anth201

Número de Mensagens : 17
Data de inscrição : 21/05/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0

Voltar ao Topo Ir em baixo

Qualidade Re: [Tutorial] Máquina de estados finitos

Mensagem por Conteúdo patrocinado Hoje à(s) 12:37


Conteúdo patrocinado


Voltar ao Topo Ir em baixo

Ver o tópico anterior Ver o tópico seguinte Voltar ao Topo

- Tópicos similares

 
Permissão deste fórum:
Você não pode responder aos tópicos neste fórum