Movimentação com máquina de estados finitos

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

Movimentação com máquina de estados finitos

Mensagem por fernando.munizerthal em Sab 21 Dez 2013, 01:21

Boa noite a todos.

Vim estudando, na verdade lendo bastante sobre Maquina de estados finitos. Porém não consigo iniciar trabalhos com ela no meu jogo, e percebi que ela é a maneira mais correta e mais elegante de se trabalhar com movimentações e IA.

Gostaria muito de uma ajuda com a movimentação padrão de um monstro que estou tentando fazer..

Antes de mais nada, o contexto:

Jogo de plataforma, onde preciso fazer instâncias de um monstro andarem nas plataformas de um lado para o outro porém de maneiras diferentes, isso é: Parando, depois continuando para uma direção randômica, seriam 5 ações: parado, andando_Direita, andando_Esquerda, levando_Golpe, atacando.

A minha ideia era usar a máquina com um Script para cada uma destas ações e utilizar a maquina, porém eu acabei lendo tanto que confundi muito minha cabeça, gostaria de um exemplo funcional deste movimento, pode ser apenas um cubo levando estas direções dez de que seja feito com maquinas, preciso de algo para ter um norte.

Se ficou confuso de mais por favor, perguntem, preciso muito de ajuda.

Obrigado a todos.

Ps.: Estou usando Game Maker Studio uma versão Free.

fernando.munizerthal

Número de Mensagens : 76
Idade : 22
Data de inscrição : 17/10/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Movimentação com máquina de estados finitos

Mensagem por saim em Sab 21 Dez 2013, 22:00

Me parece que o problema da maioria do pessoal com a MEF é a transição entre os estados - o cerne da máquina!
É o seguinte, a ação em cada estado é pouca coisa. Andar pra direita é só "sprite_index = spr_andando; x += vel_horiz", mas e aí, como que muda pra outro estado? Como que sai disso?

Condições. Se colide com o objeto de fim de curso, muda o estado pra "andando pra esquerda". Se o player dá um golpe, o próprio player obriga o estado pra "tomando lenhada". Se.
Essas condições são verificadas no próprio script do estado. Se existem condições que são mandatórias (cair se não tem chão embaixo, morrer se energia <= 0, etc.), um script pode ser criado pra rodar no começo de todos os estados e interromper o que quer que o estado contenha.

Isso é um tiro que pode sair pela culatra. E se eu quiser usar um evento como condição pra mudar o estado? Por exemplo, o evento "fora da room" ou o evento "fim da animação"? Aí, temos um problema. A solução, acredito, é fazer uma gambiarra.
Esses eventos podem ter códigos que se enfiam dentro dos estados, talvez verificando se o estado aceita esse evento específico. Ou então o evento muda o valor de uma variável que vai ser verificada dentro do evento.
Acho que estou complicando mais do que explicando. Vamos direto pro exemplo e se acontecer essa situação, você me fala.

Seu exemplo pode ser feito assim:
(como sou contra dar o código pronto, sem te dar uma chance de fazer por conta própria, vou deixar só a lógica, por enquanto)
estado_andando
Código:
se (tempo_no_estado == 1){
sprite_index := spr_andando; image_speed := 1; image_xscale := sinal_direção;
}
x += sinal_direção * vel_horiz; // ação, mesmo, é só isso

se (chegou_na_parede){
sinal_direção *= -1; // pra começar a andar já pro outro lado
proximo_estado := estado_parado;
return(estado_proximo)
} // se continuar a rodar as próximas linha, é porque NÃO chegou na parede

se (o player está ao alcançe){
próximo_estado := estado_ataca;
return(estado_proximo);
}

se (tempo_no_estado >= 60){ // depois de um tempo mínimo
se (floor(random(200)) == 0){ // uma chance relativamente pequena de parar sozinho
sinal_direção := choose(-1, 1); // direção aleatória
proximo_estado := estado_parado;
return(estado_proximo);
}
}

// se nenhuma das condições acima foi satisfeita, o código chega até aqui e o estado não se altera
return(estado_continua);

No estado parado, a coisa é mais fácil; depois de um tempo ele começa a andar - e só!
Código:
se (tempo_no_estado == 1){
sprite_index := spr_de_boa; image_speed := 0.8; // exemplo
}

se(tempo_no_estado >= 30 && floor(random(100)) == 0){
// se cansou de descansar
proximo_estado := estado_andando;
return(estado_proximo)
}

// mais uma vez: se o código chegou até aqui, é porque é pra continuar no estado
return(estado_continua);

Note que ele estando parado, NÃO VAI atacar o player. Você pode mudar isso, mas deixei assim pro jogador ter uma brecha pra atacar (e pra você entender que esquecer uma condição num estado impede ela de acontecer).

Agora, o estado_atacando
Código:
se (tempo_no_estado == 1){
sprite_index := spr_atacando; image_speed := 1;
}

if (place_meeting(x, y, obj_player)){ // se acertou o player
// causa um monte de consequencias no player
vitima = obj_player;
vitima . vida -= 1;
vitima . estado := estado_apanhando;
vitima . tempo_no_estado := 0;
vitima . tempo_invencivel := 30;
// etc, etc.
}

se (tempo_no_estado == 40){ // ou se acabou a animação
proximo_estado := estado_andando
return(estado_proximo);
}

return(estado_continua);
viu como o monstro alterou o estado do player? Você viu só? Isso significa que, no player, eu não preciso me preocupar com "levar o golpe". Por outro lado, significa que o obj_player, ao me acertar, vai ter que mudar meu estado pra estado_apanhando. O estado_apanhando é mais ou menos assim:
Código:
if (tempo_no_estado ==1){
sprite_index := spr_dor; image_speed := 0; // só uma imagem
}

if (tempo_no estado == 37){ // arredondar, eu?
proximo_estado == estado_andando;
return(estado_proximo);
}

return(estado_continua);

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

Re: Movimentação com máquina de estados finitos

Mensagem por fernando.munizerthal em Seg 23 Dez 2013, 08:34

Nossa, acho que entendi bem a parte de transição agora.. Infelizmente só poderei testar hoje a noite..
Quanto a lógica entendi bem por enquanto, tenho apenas algumas dúvidas olhando o código por cima sem por no GM

No primeiro if você colocou, "(tempo_no_estado == 1)", como eu consigo o tempo que está no estado? Eu li sobre isso acho, é um método das MEFs certo?

No segundo if você colocou, "(chegou_na_parede)", Eu não entendi como você usa essa variável, ela é criada no create, e depois verificada com um evento colision? Ou é de outra maneira?

No momento em que o player leva o dano, você colocou "vitima . estado := estado_apanhando;" Eu tenho um problema em chamar os estados na verdade, não diria que é na transição e sim na maneira de chamar, da nomenclatura disso.. No caso, eu não sei bem como chamar a MEF pela primeira vez para iniciar o primeiro estado, eu sei que tenho que colocar um código no step, porém esse código tem a primeira linha assim: "maquina_de_estados(st_default);" porém isso causa erro no meu código, eu não sei se "maquina_de_estados" deve ser um script ou o que. Acredito que resumindo a dúvida seria, como iniciar a MEF para ele começar a seguir a lógica dos códigos e transitar pelos estados..

Muito obrigado pela resposta completa de antes Saim você é demais hehe

fernando.munizerthal

Número de Mensagens : 76
Idade : 22
Data de inscrição : 17/10/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Movimentação com máquina de estados finitos

Mensagem por saim em Seg 23 Dez 2013, 11:10

fernando.munizerthal escreveu:No primeiro if você colocou, "(tempo_no_estado == 1)", como eu consigo o tempo que está no estado? Eu li sobre isso acho, é um método das MEFs certo?
É uma variável da máquina, mesmo. Se você for esmiuçar o script, vai notar que no primeiro step ela é declarada, a cada mudança de estado ela é zerada, e soma-se 1 ao seu valor a cada step.
Eu não estou com o script aberto aqui, mas lembro que tinha uma peculiaridade com relação ao seu valor. O lance é que o lógico seria ter que testar se o valor é zero, não um, no primeiro step de cada estado... mas quando a gente muda de estado e o tempo é zerado, logo no final da transição a coisa já volta ao normal e é somado um ao tempo... tem que dar uma olhada nisso, com calma.

fernando.munizerthal escreveu:No segundo if você colocou, "(chegou_na_parede)", Eu não entendi como você usa essa variável, ela é criada no create, e depois verificada com um evento colision? Ou é de outra maneira?
Não é uma variável. Como eu disse, não estou te dando o código pronto, mas uma idéia geral da lógica. Acho que eu devia ter usado "se (não dá pra avançar)", ficaria mais claro.
Eu não conheço as condições que você quer usar pra fazer ele parar, então inventei essa. Substitua pela condição real do jogo.

No momento em que o player leva o dano, você colocou "vitima . estado := estado_apanhando;" Eu tenho um problema em chamar os estados na verdade, não diria que é na transição e sim na maneira de chamar, da nomenclatura disso.. No caso, eu não sei bem como chamar a MEF pela primeira vez para iniciar o primeiro estado, eu sei que tenho que colocar um código no step, porém esse código tem a primeira linha assim: "maquina_de_estados(st_default);" porém  isso causa erro no meu código, eu não sei se "maquina_de_estados" deve ser um script ou o que. Acredito que resumindo a dúvida seria, como iniciar a MEF para ele começar a seguir a lógica dos códigos e transitar pelos estados..
É bem por aí. Esse comando do step é um script (a máquina em si) que executa outros scripts (os estados). O st_default é um estado que inicializa o objeto, o que não é muito legal por causa do evento draw...
um pouco off-topic:
Eu explico: ao inicializar um objeto, a gente costuma definir um monte de variáveis a serem usadas no draw, por exemplo o sprite_index (dá pra fazer isso direto na configuração do objeto, mas é só um exemplo). Se você coloca qualquer coisa no draw, certamente vai se referir a variáveis do objeto, certo? Bom, agora a coisa fica mais chatinha: o primeiro draw do objeto acontece ANTES do primeiro step, mas DEPOIS do create. Se você define variáveis no primeiro step e chama elas no draw, vai dar erro (porque, no primeiro draw, essas variáveis ainda não estão definidas). Dá pra contornar com um teste simples (inicializado := false, no create, true no st_default, desenha só se inicializado == true), mas é meio desanimador ter que usar o create numa lógica da MEF.
De qualquer forma, o GMS tem alguma coisa contra essa MEF (e, como deu pau no meu GMS, não consigo lembrar bem o que é). Acho que é alguma função que caducou. Dá pra contornar usando o create, então essa falha da MEF acaba vindo a calhar - é uma boa desculpa pra usar o create pra inicializar o objeto, como deve ser - mas algumas alterações acabam sendo necessárias... e eu não lembro quais são...
Tenho uma formatação da minha máquina programada pro começo do ano, aí eu restabeleço o GMS e volto a programar nele.
Bom, continuando com a explicação, o máquina foi feita pra funcionar num objeto fechado no mundinho dele, o que pode causar um monte de problemas. Porque nossos objetos não são fechados no mundinho deles. É muito mais fácil uma bala te mudar pro estado st_leva tiro e um raio te mudar pro st_leva_choque do que verificar, em todos os estados, se está colidindo com uma bala, se está na linha do raio, se está na linha de ação de uma granada, se caiu na água, se etc.
Aí, eu faço isso. Coloco a bala pra mudar o estado. Ao fazer isso, além de definir qual o script do próximo estado, tenho que tomar cuidado com um monte de outras variáveis: tempo_no_estado, arg0, 1, 2, n... acho que é só isso! Tem mais alguma variável da máquina? Acho que não. Ainda tem as variáveis específicas, como "invencível", "vida", sei lá.

No final, a máquina não é uma solução tão mágica assim. Mas eu ainda gosto muito de usá-la.

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

Re: Movimentação com máquina de estados finitos

Mensagem por fernando.munizerthal em Seg 23 Dez 2013, 13:03

Okay, esclareceu bastante coisa de novo, hehe Mas escureceu algumas.. Na verdade, ainda não entendi a última parte..

Okay é um script a máquina em sí.. Porém quando eu coloco para rodar da um erro, informando que não encontrou ele..
Acho que é o que você colocou:

saim escreveu:De qualquer forma, o GMS tem alguma coisa contra essa MEF (e, como deu pau no meu GMS, não consigo lembrar bem o que é). Acho que é alguma função que caducou. Dá pra contornar usando o create, então essa falha da MEF acaba vindo a calhar - é uma boa desculpa pra usar o create pra inicializar o objeto, como deve ser - mas algumas alterações acabam sendo necessárias... e eu não lembro quais são...

Eu não entendi como contornar.. Hoje a noite quando eu chegar em casa após o serviço, vou tentar usar as lógicas passadas.. E então te darei um retorno, mas acredito que não vou conseguir testar devido a este erro que ocorre.

Vou te passar o cenário do jogo para ficar mais claro..

É um jogo de plataforma, no momento quero apenas fazer duas plataformas com dois monstros, esses utilizando as MEFs para fazerem sua movimentação..

No jogo em geral eu quero usar as MEFs apenas para movimentar os inimigos e os NPCs.. Não sei se é uma tática boa, mas o player e o resto vou utilizar lógica sem as MEFs.. Será que é uma boa alternativa?

Obrigado pelo seu tempo novamente Saim.

fernando.munizerthal

Número de Mensagens : 76
Idade : 22
Data de inscrição : 17/10/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Movimentação com máquina de estados finitos

Mensagem por saim em Seg 23 Dez 2013, 13:33

Ah, você está usando é o GMS? Tem alguma coisa que dá errado nele, mesmo, mas não consigo lembrar o que é! Tenho quase certeza que é uma das funções que não dá mais pra usar.

Achei! É variable_exists! Isso não dá pra usar essa função no GMS!
Então, ao invés de verificar se as variáveis existem, o negócio é garantir que elas existam. O que eu faço é criar um script de inicializar a MEF e jogar no create event.
Código:
indice_do_estado := argument0; //estado default state a ser executado
proximo_estado = argument0 //próximo estado a ser executado
arg0 := 0;
arg1 := 0;
arg2 := 0;
arg3 := 0;
arg4 := 0;
arg5 := 0;
arg6 := 0;
arg7 := 0;
limpa_argumentos := 0;
Aí, tira a parte que verifica isso, do script.

Ufa, estava com medo de que a função caduca fosse script_execute. Nesse caso, eu teria que encontrar a solução que tivesse sido dada, porque agora não consigo pensar numa forma de não usar ela...

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

Re: Movimentação com máquina de estados finitos

Mensagem por fernando.munizerthal em Seg 23 Dez 2013, 13:54

Sim uso GMS hehe
Entendi, farei isso, mas não era só ai que dava erro não, no meu projeto da erro na parte do código "maquina_de_estados(st_default);" mais especificamente na parte "maquina_de_estados" que é a primeira parte colocada do código do step, o erro diz, "Script não encontrado".. e não roda o projeto.

Eu acho que devo estar esquecendo algo bem básico que eu não deveria esquecer haha.

Obrigado novamente pela paciência.

fernando.munizerthal

Número de Mensagens : 76
Idade : 22
Data de inscrição : 17/10/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Movimentação com máquina de estados finitos

Mensagem por saim em Seg 23 Dez 2013, 15:11

Bom, se ele diz que o script não existe, então ele não deve existir... Você criou o script (igual o do tuto) e deu o nome de "maquina_de_estados"?

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

Re: Movimentação com máquina de estados finitos

Mensagem por fernando.munizerthal em Seg 23 Dez 2013, 18:47

Não, eu não sei o que por nesse script, não achei o código dele, eu não devo ter lido certo, hehehe deve ser isso Razz

Tem como me mostrar o que vai nele? ou a parte do post em que ele está?

Muito obrigado novamente Saim, acho que essa é a última dúvida antes de por a mão na massa e ver como fica..

Abraços

fernando.munizerthal

Número de Mensagens : 76
Idade : 22
Data de inscrição : 17/10/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Movimentação com máquina de estados finitos

Mensagem por saim em Seg 23 Dez 2013, 22:16

Heheh. Está no final do tuto, é a última caixa de código.
Agora é que vão começar a surgir as dúvidas...

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

Re: Movimentação com máquina de estados finitos

Mensagem por fernando.munizerthal em Seg 23 Dez 2013, 22:34

Okok era isso mesmo que eu tava me confundindo só para ter certeza então, isso é o código da MEF:

MEFs Code:
//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;
}

Nesse caso eu já retirei a parte que você informou que não funciona no GMS, que seria:

Código que da erro no GMS:
//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
}

E criei um outro script ao qual dei o nome de run_NecessaryCode_MEFs e coloquei o seguinte:

run_NecessaryCodes_MEFs:
indice_do_estado := argument0; //estado default state a ser executado
proximo_estado = argument0 //próximo estado a ser executado
arg0 := 0;
arg1 := 0;
arg2 := 0;
arg3 := 0;
arg4 := 0;
arg5 := 0;
arg6 := 0;
arg7 := 0;
limpa_argumentos := 0;

Agora eu chamo primeiro isso no create, nos argumentos eu passo os estados que eu vou criar (que nada mais são do que scripts com as lógicas dos movimentos, como a que você me passou no início).. e depois chamar no evento Step a máquina virtual.. Isto?

fernando.munizerthal

Número de Mensagens : 76
Idade : 22
Data de inscrição : 17/10/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Movimentação com máquina de estados finitos

Mensagem por fernando.munizerthal em Seg 23 Dez 2013, 23:05

Mais algumas dúvidas que eu tive tentando programar como você disse:

Agora é que vão começar a surgir as dúvidas...

hehe era verdade.. Bom então, voltando aos códigos anteriores e suas lógicas você colocou uma variável que se chama sinal_direção eu não sei como usar ela.. Mad Não entendi nada sobre ela na verdade.. Então estou perdido já por causa dela..

Segundo.. A variável tempo_no_estado está dando um erro, não sei se esqueci de estânciar ela ou algo deste tipo.. Eu lembro que vocÊ comentou:

Se você for esmiuçar o script, vai notar que no primeiro step ela é declarada, a cada mudança de estado ela é zerada, e soma-se 1 ao seu valor a cada step.

Porém não sei bem como ou onde declarar ela..

E por último, acho que expliquei mal, mas eu teria dois sprites um para a direita e outro para esquerda, talvez por não ter entendido o que postei acima eu posso estar me confundindo, mas não sei como faço essa mudança de sprite.. seria no estado_andando nos ifs:

Código:
if(tempo_no_estado == 1){
    sprite_index := ietiAndandoEsquerda_sprt;
    image_speed := 1;
    image_xscale := image_xscale + image_xscale;
        }

Código:
if (chegou_na_parede){
    x *= -1; // pra começar a andar já pro outro lado
    proximo_estado := estado_parado;
    return(estado_proximo)
        }

Código:
if(tempo_no_estado >= 60){ // depois de um tempo mínimo
    if(floor(random(200)) == 0){ // uma chance relativamente pequena de parar sozinho
        x := choose(-1, 1); // direção aleatória
        proximo_estado := estado_parado;
        return(estado_proximo);
    }
        }

Desculpe te encher de dúvidas assim Saim, mas estou muito ansioso para fazer esse cara andar hehehe Obrigado pela paciência.

fernando.munizerthal

Número de Mensagens : 76
Idade : 22
Data de inscrição : 17/10/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Movimentação com máquina de estados finitos

Mensagem por saim em Ter 24 Dez 2013, 08:46

fernando.munizerthal escreveu:Okok era isso mesmo que eu tava me confundindo só para ter certeza então, isso é o código da MEF:
(...)
Esse mesmo
fernando.munizerthal escreveu:
E criei um outro script ao qual dei o nome de run_NecessaryCode_MEFs e coloquei o seguinte:
(...)
Quase. Já adiantando a dúvida posterior, esquecemos de incluir o tempo_no_estado, aqui.

fernando.munizerthal escreveu:Agora eu chamo primeiro isso no create, nos argumentos eu passo os estados que eu vou criar (que nada mais são do que scripts com as lógicas dos movimentos, como a que você me passou no início).. e depois chamar no evento Step a máquina virtual.. Isto?
Quase. O primeiro argumento desse "run_necessary..." é o primeiro estado do objeto. Não tem mais argumentos. Aí, no step, a MEF nem precisa de argumentos, porque o primeiro estado já foi definido no create.
O resto dos argumentos (arg1 a 7) já entra declarado. São variáveis usadas como argumentos dentro do script da máquina. Você teria que dar uma esmiuçada na máquina pra entender direito...

fernando.munizerthal escreveu:você colocou uma variável que se chama sinal_direção eu não sei como usar ela.. :xNão entendi nada sobre ela na verdade.. Então estou perdido já por causa dela..
Esqueça ela. Eu usei pra economizar um estado e uma sprite, mas isso não é importante. É que pra andar de um lado pra outro, eu usava o mesmo estado e a mesma sprite, usando image_xscale. Pro jogo saber pra que lado andar, eu usava uma variável que assumia os valores "1" ou "-1".
Se você prefere usar dois estados e duas sprites, não vai precisar dela.

fernando.munizerthal escreveu:A variável tempo_no_estado está dando um erro, não sei se esqueci de estânciar ela ou algo deste tipo.. Eu lembro que vocÊ comentou:

Se você for esmiuçar o script, vai notar que no primeiro step ela é declarada, a cada mudança de estado ela é zerada, e soma-se 1 ao seu valor a cada step.

Porém não sei bem como ou onde declarar ela..
Ver acima. É pra declarar no create, mesmo, eu esqueci dela...  envergonhado 

fernando.munizerthal escreveu:E por último, acho que expliquei mal, mas eu teria dois sprites um para a direita e outro para esquerda, talvez por não ter entendido o que postei acima eu posso estar me confundindo, mas não sei como faço essa mudança de sprite..
Ver acima. Se prefere usar duas sprites, vai precisar fazer seus próprios estados (em qualquer hipótese, você vai fazer seus próprios estados: os que eu passei não foram feitos pra funcionar de verdade).
Mas, quanto à mudança de sprite, é só mais uma coisa a ficar de olho. Eu gosto de fazer a mudança no início do estado, então olho o tempo_no_estado. Se for 1, mudo a sprite pro valor adequado. É uma prática que me garante que todos os estados terão sprites adequadas.
Mas você pode definir a sprite a partir do código imediatamente anterior à mudança, ou seja, no estado que vai definir o próximo estado. Tipo:
Código:
se (condição pra mudança foi atingida){
proximo_estado := coloque_um_estado_aqui;
sprite_index := sprite_adequado_ao_proximo_estado; // aqui eu defino o estado antes dele começar
return(estado_proximo)
}
O problema disso é que quando você tem dúzias de estados, fica mais difícil descobrir em qual deles você esqueceu de definir a sprite do estado seguinte. Do meu jeito, se um estado está com a sprite errada, é nele que está o erro.

Olha, se você não pegou ainda a MEF, recomendo muito dar uma relida no tutorial, principalmente pra você entender o que significa cada variável.[/quote]

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

Re: Movimentação com máquina de estados finitos

Mensagem por fernando.munizerthal em Ter 24 Dez 2013, 14:31

Boa tarde Saim, hoje estarei em casa a tarde então poderei testar melhor as coisas..

Realmente estou ficando com dor de cabeça já, acabei de reler o tutorial, eu entendi a lógica mas não consigo por em prática nada do que conversamos.

Eu vou lhe mostrar como está o meu projeto de teste:

Sprites:
   Existem os sprites do monstro parado para a direita e para a esquerda.
   Existe um sprite para o chão e um para a parede.
Objetos:
   Existem os objetos monstro, parede e chão.
Scripts:
   Criei os scripts:
       andando_Direita
       andando_Esquerda
       parado
       maquina_de_Estados
       run_MEFs
       st_default

Okay, agora no create do objeto monstro eu coloco:

Código:

colision = false;
tempo_no_estado := 0;
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
run_MEFs(estado_andando_direita); // Acredito que aqui seria st_default

Okay, não sei o que por no st_default.. e o código do run_MEFs já foi passado..
No Step do monstro eu passo: maquina_de_estados(estado_andando_direita); // e aqui também


Em cada um dos estados eu coloco no momento de ir para outro, no meu caso eu gostaria que isso fosse aleatório ou quando batesse na parede..

Para essa mudança devo usar,        
Código:
proximo_estado := estado_andando_esquerda;
return(proximo_estado);

Mas nada funciona hehehe
Eu estou realmente muito perdido, tentei abrir o seu exemplo, do seu tutorial, mas ele vem sem o script da máquina, e eu não consigo coloca-lo pois a versão free só permite x scripts e já estourou esse limite..

fernando.munizerthal

Número de Mensagens : 76
Idade : 22
Data de inscrição : 17/10/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Movimentação com máquina de estados finitos

Mensagem por fernando.munizerthal em Ter 24 Dez 2013, 15:47

Saim, fiquei fuçando aqui, e consegui fazer ele andar um pouquinho para um lado hehe Very Happy
Mas não me parece estar muito certo.. hehehe

Eu mantive o código parecido mas retirei o st_default, percebi que era o mesmo que o meu run ou similar, tinha a mesma função..

Então no create eu uso isto: run_MEFs(estado_parado);
No estado parado eu tenho:
*Lembrando que o tempo_no_estado eu coloco igual a um no run_MEFs

Código:
if(tempo_no_estado == 1){
    sprite_index := sprite2;
    image_speed := 0.3; // exemplo
}

if(tempo_no_estado >= 50){
    // se cansou de descansar
    proximo_estado := estado_andando_direita;
    return(estado_proximo)
}

// mais uma vez: se o código chegou até aqui, é porque é pra continuar no estado
return(estado_continua);

E depois no step eu uso: maquina_de_estados(estado_parado);

Após ficar um tempo parado ele vai para o estado_andando_direita, porém ai da um problema ele pula direto para o final de seu trajeto..

Segue o estado_andando_direita:
Código:
if(tempo_no_estado == 0){
    show_debug_message("enctrou");
    sprite_index := ietiAndandoDireita_sprt;
    image_speed := 0.3;
    eti_Obj.x = eti_Obj.x + 0.5;
}

if (eti_Obj.x == obj_colisao.x){
    x *= -1; // pra começar a andar já pro outro lado
    proximo_estado := estado_andando_esquerda;
    return(estado_proximo)
}

if(tempo_no_estado >= 60){ // depois de um tempo mínimo
    if(floor(random(200)) == 0){ // uma chance relativamente pequena de parar sozinho
        proximo_estado := estado_andando_esquerda;
        return(estado_proximo);
    }
}

// se nenhuma das condições acima foi satisfeita, o código chega até aqui e o estado não se altera
return(estado_continua);

Ele para quando bate na parede.. Isto está certo no momento, mas eu gostaria de ver ele andando ele simplesmente sai da posição e vai para a outra mudando de sprite para o sprite andando para a direita..

Não sei o que fazer..
Obrigado novamente Saim

fernando.munizerthal

Número de Mensagens : 76
Idade : 22
Data de inscrição : 17/10/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Movimentação com máquina de estados finitos

Mensagem por fernando.munizerthal em Ter 24 Dez 2013, 16:55

Caraaa, acho que funcionou *o* hahaha
Só tem um problema ocorrendo, uma instância do objeto segue a outra, o sprite dela faz movimentos diferentes mas ele continua seguindo a outra instância :X

    ---Editado---

Funcionou *-*

Obrigado pela ajuda Saim, vou postar como ficou, se você puder dar uma olhada e ver se está certo.

Scripts:
-    maquina_de_estados

Código:
//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;
  }


-    run_MEFs

Código:
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

tempo_no_estado = 1;
indice_do_estado := argument0; //estado default state a ser executado
proximo_estado = argument0 //próximo estado a ser executado
arg0 := 0;
arg1 := 0;
arg2 := 0;
arg3 := 0;
arg4 := 0;
arg5 := 0;
arg6 := 0;
arg7 := 0;
limpa_argumentos := 0;

return(proximo_estado)

-    run_MEFs

estado_andando_direita

Código:
if(tempo_no_estado == 0){
    sprite_index := ietiAndandoDireita_sprt;
   image_speed := 0.3;
}

x = x + 2;
    
if (place_meeting(x,y, obj_colisao)){
    proximo_estado := estado_andando_esquerda;
    return(estado_proximo)
}

if(tempo_no_estado >= 60){ // depois de um tempo mínimo
    if(random(200) >= 100){ // uma chance relativamente pequena de parar sozinho
        proximo_estado := estado_andando_esquerda;
        return(estado_proximo);
    }else
    {
        proximo_estado := estado_parado;
        return(estado_proximo)
    }
}

// se nenhuma das condições acima foi satisfeita, o código chega até aqui e o estado não se altera
return(estado_continua);

estado_parado

Código:
if(tempo_no_estado == 1){
    sprite_index := sprite2;
   image_speed := 0.3; // exemplo
}

if(tempo_no_estado >= 50){
    if(random(200) >= 100)
    {
     proximo_estado := estado_andando_direita;
    }
    else{
        proximo_estado := estado_andando_esquerda;
    }
    return(estado_proximo)
}

// mais uma vez: se o código chegou até aqui, é porque é pra continuar no estado
return(estado_continua);

estado_andando_esquerda

Código:
if(tempo_no_estado == 0){
    sprite_index := ietiAndandoEsquerda_sprt;
   image_speed := 0.3;
}

x = x - 2;

if (place_meeting(x,y, obj_colisao)){
    proximo_estado := estado_andando_direita;
    return(estado_proximo)
}

if(tempo_no_estado >= 60){ // depois de um tempo mínimo
    if(random(200) >= 100){ // uma chance relativamente pequena de parar sozinho
        proximo_estado := estado_andando_direita;
        return(estado_proximo);
    }else
    {
        proximo_estado := estado_parado;
        return(estado_proximo)
    }
}

// se nenhuma das condições acima foi satisfeita, o código chega até aqui e o estado não se altera
return(estado_continua);

Objetos
-    objMonstro
--        No Create: run_MEFs(estado_parado);
--        No Step: maquina_de_estados(estado_parado);

Criei mais dois objetos um parede e um chão, são insignificantes, são sólidos e sem código, ficou nisso..
Está bom dessa maneira?

Vou começar a fazer o monstro atacar o player em breve.

Obrigado por toda a ajuda Saim, aguardo a avaliação de como ficou o código.

fernando.munizerthal

Número de Mensagens : 76
Idade : 22
Data de inscrição : 17/10/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Movimentação com máquina de estados finitos

Mensagem por saim em Ter 24 Dez 2013, 17:41

Está legal. Tem muita coisa que eu faria diferente, mas diferente não significa "mais certo".

Lembre-se de colocar o código pra atacar o player em todos os estados que você quer essa possibilidade. O recomendável, mesmo, é fazer um arquivinho de texto descrevendo todas as possibilidades de cada estado, antes de escrever qualquer um deles, mas isso ninguém faz - acaba dando quase o mesmo trabalho que programar, então todo mundo (inclusive eu) acaba achando mais fácil programar direto.

Parabéns, está certinho!

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

Re: Movimentação com máquina de estados finitos

Mensagem por fernando.munizerthal em Ter 24 Dez 2013, 17:52

Sim eu estou começando isso, mas para empurrar o player eu não teria que fazer o player também com MEF?

Bah obrigado mesmo Saim pela ajuda e por dizer que ta legal hehe ><
Acho que esse tópico vai ajudar bastante gente também Smile

E me ajudou muito hehehe espero aprender muito ainda de MEF gostei bastante
Até mais.

fernando.munizerthal

Número de Mensagens : 76
Idade : 22
Data de inscrição : 17/10/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Movimentação com máquina de estados finitos

Mensagem por fernando.munizerthal em Ter 24 Dez 2013, 18:44

HAHA consegui fazer ele dando dano no player.. Mas ta um pouco errado
Não sei se está correto também o resto, mas por enquanto está funcionando assim:

Ele empurra o player, porém o inimigo perde o movimento até o fim do dano do player..

Nos estados "estado_andando_esquerda" e "estado_andando_direita"
Eu coloquei o seguinte if:

Código:

if(place_meeting(x,y, Player_Obj))
{
    proximo_estado := estado_ataque;
    return(estado_proximo)
}

E então no estado_ataque coloquei este isto:

Código:
if(tempo_no_estado == 1){
    sprite_index := ietiParado_sprt;
    image_speed := 0.3; // exemplo
    global.levandoDano = true;
}

show_debug_message("entrou");
Player_Obj.x = Player_Obj.x - 5;
Player_Obj.y = Player_Obj.y - 10;

if(tempo_no_estado >= 40)
{
    global.levandoDano = false;
    proximo_estado := estado_andando_direita;
    return(estado_proximo);
}

return(estado_continua);

Ai não está tudo que ele vai fazer quando dar o dano na verdade..
E neste caso é só quando o player pecha nele..

O que não está funcionando é que o monstro para de fazer ações pois entrou em um estado onde não realiza movimentos..

Ainda preciso fazer ele levando dano, ele morrendo e ele indo atacar o player após levar dano..

Como faço para ele continuar andando mesmo estando em outro estado?

Obrigado Saim

fernando.munizerthal

Número de Mensagens : 76
Idade : 22
Data de inscrição : 17/10/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Movimentação com máquina de estados finitos

Mensagem por saim em Sab 28 Dez 2013, 23:19

Antes de qualquer coisa, vou te pedir pra não postar duas vezes seguidas. É contra as regras da comunidade. Você pode editar o post anterior, colocar um texto explicando que ele foi editado e quando eu ler, vou saber que houve um delay entre um post e outro. Se estiver com receio de eu ter lido e não respondido e quiser garantir que eu leia o segundo post, me mande uma MP explicando "olha, postei, não sei se você viu, mas de qq forma, postei de novo".

Bom, eu realmente recomendo fazer o player com a MEF. Mas não atuar sobre o player através do monstro senão pra alterar o estado.
Pense da seguinte forma: se você passa 40 steps nesse estado, tem a possibilidade do player ficar alguns (pelo menos mais que um) steps em contato contigo. Esse deslocamento muito rápido dá um visual estranho ao jogo.
O que eu faria é criar um estado "tomando lenhada" no player mesmo e fazer o monstro definir o índice do estado do player como o nome desse estado (estou com um pouco de sono, se eu não estiver sendo claro, me avise).

Mas não foi isso que você perguntou. Você perguntou como o MONSTRO deve continuar andando sem estar no estado "andando". Bom, em todos os estados em que ele anda, ele deve ter um "x += valor". Cada estado deve conter todas as ações que o bicho vai fazer. Isso gera um pouco de redundância, certo, mas facilita a programação - e você pode criar um script "anda pra direita" e chamá-lo em diversos estados, também.
Eu considero que um estado "atacando" precisa de uma sprite animada "atacando", de modo que o monstro não faça nada, mas PAREÇA estar fazendo alguma coisa (balançando uma espada, batendo as garras, soprando fogo, sei lá).

O que me remete de volta ao estado "apanhando" do player. Temos a tendência de deixar sempre o jogador no controle do player. Mas ao criarmos um estado em que ele deve se movimentar por conta da programação, não estamos tirando ele do controle? Sim, por um tempo ele fica só olhando pra tela, com cara de raiva, martelando o controle, mas isso é legal. Pense em quando o scorpion te dá um "get over here!". Você fica fulo, vendo sua personagem rodando enquanto o cara pensa no que vai fazer pra te ferrar - mas sem isso, o jogo não teria tanta graça.

Mas estou divagando. Quando estou com sono, meu cérebro entra em loop.

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

Re: Movimentação com máquina de estados finitos

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


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