{"id":64,"date":"2025-04-25T19:35:00","date_gmt":"2025-04-25T17:35:00","guid":{"rendered":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/?p=64"},"modified":"2025-04-24T14:55:53","modified_gmt":"2025-04-24T12:55:53","slug":"automatas-no-arcade","status":"publish","type":"post","link":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/2025\/04\/25\/automatas-no-arcade\/","title":{"rendered":"Aut\u00f3matas no arcade"},"content":{"rendered":"\n<p>Cando estamos a xogar a un videoxogo notamos como hai personaxes ou outros elementos que se comportan de modo distinto segundo o que estea a pasar. Un inimigo pode estar seguindo un cami\u00f1o e correr tras do xogador cando o ve, o propio personaxe do xogador pode subirse a un veh\u00edculo e comezar a conducilo.<\/p>\n\n\n\n<p>Para calquera elemento do xogo hai unha serie de procesos que deben acontecer en cada imaxe debuxada na pantalla. Para un inimigo, por exemplo, debe de actualizarse a animaci\u00f3n sexa de andar ou correr, os sons dos seus pasos, o obxecto de f\u00edsicas que dita como se comportar\u00e1 ao empurrar unha porta e moitos outros.<\/p>\n\n\n\n<p>Cando o estado actual da entidade cambia, o comportamento de todos os sistemas cos que interact\u00faa debe mudar \u00e1 vez. A maneira trivial de levar isto a cabo ser\u00eda que cada sistema tivese unha serie de condicionais e todos dependesen dunha mesma variable:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>enum Estado {ANDAR, PERSEGUIR, CONDUCIR}\nvar estado_actual: Estado = Estado.ANDAR\n\nfunc _input(event: InputEvent):\n  if estado_actual == Estado.CONDUCIR:\n    # modificar aceleraci\u00f3n do auto\n  else:\n    # modificar posici\u00f3n do xogador\n\nfunc _process(delta):\n  if estado_actual == Estado.CONDUCIR:\n    # xestionar animaci\u00f3n do veh\u00edculo\n  elif estado_actual == Estado.ANDAR\n    # animaci\u00f3n de andar, movemento lento\n  else:\n    # animaci\u00f3n de correr, movemento r\u00e1pido\n\nfunc _physics_process(delta):\n  # igual que arriba, decidir segundo \"estado_actual\"<\/code><\/pre>\n\n\n\n<p>Xa no exemplo de arriba, no que non hai c\u00f3digo real que realize nada, xa vemos o inc\u00f3modo que poder\u00eda resultar. A maiores, \u00e9 habitual que para cada estado haxa unha serie de variables que non son necesarias nas outras, o que far\u00eda a mara\u00f1a a\u00ednda m\u00e1is complexa.<\/p>\n\n\n\n<p>Por \u00faltimo, tomando un dos exemplos do xogo Metarcade, que far\u00edamos se un dos personaxes fose en cadeira de rodas? Quizais engadir un &#8220;if&#8221; m\u00e1is nos casos de correr e andar. Ou facer unha subclase que en estes estados en concreto rectificase algunha decisi\u00f3n da clase pai?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Aut\u00f3matas<\/h2>\n\n\n\n<p>A pesar do xeral que soa o t\u00edtulo, neste artigo s\u00f3 imos ver un tipo de aut\u00f3mata en concreto a <a href=\"https:\/\/es.wikipedia.org\/wiki\/M%C3%A1quina_de_estados\" data-type=\"link\" data-id=\"https:\/\/es.wikipedia.org\/wiki\/M%C3%A1quina_de_estados\">m\u00e1quina de estados<\/a>. Deste xeito, non ser\u00eda o nodo actual o que executar\u00eda os comandos para cada estado, sen\u00f3n que s\u00f3 xestionar\u00eda en que &#8220;Estado&#8221; se encontra, e delegar\u00eda noutros nodos para executar cada caso concreto.<\/p>\n\n\n\n<p>Para isto, comezar\u00edamos por definir un script base para cada estado:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class_name State\nextends Node\n\nfunc input(event: InputEvent):\n\tpass\n\nfunc process(delta):\n\tpass\n\nfunc physics_process(delta):\n\tpass\n\nfunc enter():\n\tpass\n\nfunc exit():\n\tpass\n<\/code><\/pre>\n\n\n\n<p>Aqu\u00ed proporcionamos a calquera estado unha base que tomar para implementar todas as funcionalidades que precise. Queda un script moito m\u00e1is curto e f\u00e1cil de ler, no que s\u00f3 as variables necesarias estar\u00e1n dispo\u00f1ibles. Aparecen aqu\u00ed d\u00faas novas funci\u00f3ns que non estaban presentes no exemplo inicial, <code>enter()<\/code> e <code>exit()<\/code>. Ser\u00e1n moi c\u00f3modas para definir transici\u00f3ns entre estados ou para iniciar e parar procesos que s\u00f3 te\u00f1en que ver con un deles.<\/p>\n\n\n\n<p>Agora que sabemos como modelar un estado, quedar\u00eda por definir como xestionar o actual ou o cambio entre eles.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class_name StateMachine\nextends Node\n\n@export var initial_state: State;\n\nvar cur_state: State = null;\n\nfunc _ready():\n  set_state(initial_state)\n\nfunc set_state(new_state: State) :\n  if cur_state != null:\n    cur_state.exit()\n  cur_state = new_state\n  cur_state.enter()\n\nfunc _input(event: InputEvent):\n  cur_state.input(event)\n\nfunc _process(delta):\n  var next_state = cur_state.process(delta)\n  if next_state != null:\n    set_state(next_state)\n\nfunc _physics_process(delta):\n  cur_state.physics_process(delta)<\/code><\/pre>\n\n\n\n<p>As funci\u00f3ns <code>_input<\/code> e <code>_physics_process<\/code> soamente delegan no estado actual o seu comportamento, mentres que <code>_process<\/code> permite ao estado en execuci\u00f3n pedir un cambio a outro diferente.<\/p>\n\n\n\n<p>Unha vez definidos unha serie de estados, a xerarqu\u00eda queda como na seguinte imaxe.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"496\" height=\"175\" src=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-9.png\" alt=\"\" class=\"wp-image-92\" srcset=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-9.png 496w, https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-9-300x106.png 300w\" sizes=\"auto, (max-width: 496px) 100vw, 496px\" \/><\/figure>\n\n\n\n<p>A modo de remate, antes de comezar cos exemplos no noso xogo, gustar\u00edame recomendar o cap\u00edtulo <a href=\"https:\/\/gameprogrammingpatterns.com\/state.html\">&#8220;State&#8221; no libro Game Programming Patterns.<\/a><\/p>\n\n\n\n<h1 class=\"wp-block-heading\">M\u00e1quinas de estados en Metarcade<\/h1>\n\n\n\n<p>Hai moitas m\u00e1quinas de estado en Metarcade, pero vou mostrar o caso m\u00e1is claro, o dos NPC. Como se pode ver na seguinte imaxe, os NPC poden, entre outras variables, un di\u00e1logo (<em>Dialogue Resource<\/em>) e un cami\u00f1o a seguir (<em>Path to Follow<\/em>).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"739\" src=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-10-1024x739.png\" alt=\"\" class=\"wp-image-95\" srcset=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-10-1024x739.png 1024w, https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-10-300x217.png 300w, https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-10-768x554.png 768w, https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-10.png 1431w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Como \u00e9 de esperar, no caso de existir un di\u00e1logo a personaxe entrar\u00e1 no estado de conversa cando o xogador o invoque. No caso de non haber ning\u00fan cami\u00f1o definido de antem\u00e1n, o estado por defecto ser\u00e1 est\u00e1tico, cunha animaci\u00f3n acorde. Para os NPC que te\u00f1en un cami\u00f1o definido, estes seguirano indefinidamente mentres non sexan parados polo xogador.<\/p>\n\n\n\n<p>A m\u00e1quina de estados b\u00e1sica \u00e9 polo tanto como se mostra na seguinte imaxe. O script ra\u00edz da escea NPC config\u00faraa na carga segundo os par\u00e1metros expostos anteriormente.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"392\" height=\"171\" src=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-11.png\" alt=\"\" class=\"wp-image-96\" srcset=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-11.png 392w, https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-11-300x131.png 300w\" sizes=\"auto, (max-width: 392px) 100vw, 392px\" \/><\/figure>\n\n\n\n<p>Dos anteriores, quizais o <em>Walking<\/em> sexa o m\u00e1is interesante. A maneira de que un nodo en Godot siga un cami\u00f1o predeterminado \u00e9 definir unha curva de tipo Path3D, que \u00e9 a que exportamos arriba e ser\u00e1 est\u00e1tica, e crear un obxecto de tipo PathFollow3D que \u00e9 o que se ir\u00e1 actualizando a medida que o nodo avanza pola curva.<\/p>\n\n\n\n<p>Cando o nodo entra no estado <em>Walking<\/em>, debe buscar o punto da curva asignada que est\u00e1 m\u00e1is preto ao punto actual para inicializar o obxecto PathFollow3D. Deste modo o comportamento ser\u00e1 m\u00e1is natural. Xa que estamos definindo un estado a funci\u00f3n <em>enter<\/em>  \u00e9 a id\u00f3nea para encargarse disto.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>var path_follow: PathFollow3D\n\nfunc enter():\n  path_follow = PathFollow3D.new()\n  var offset_point =     curve_path.curve.get_closest_offset(character.position)\n  path_follow.progress = offset_point<\/code><\/pre>\n\n\n\n<p>Unha vez todo est\u00e1 preparado, a funci\u00f3n <em>process<\/em> pode encargarse do movemento.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>func process(delta):\n  path_follow.progress += speed * delta;\n  var next_position = path_follow.position;\n  character.set_position(next_pos)\n  character.rotation = path_follow.rotation\n  character.play_animation(walk_animation)<\/code><\/pre>\n\n\n\n<p>Para unha implementaci\u00f3n b\u00e1sica isto ser\u00eda suficiente. Queda bastante claro que o uso de estados foi a decisi\u00f3n correcta pola facilidade que temos para xestionar o comezo da marcha ou ao ver como a variable <em>path_follow<\/em> est\u00e1 contida aqu\u00ed sen afectar a outros estados.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Personalizaci\u00f3n do comportamento<\/h2>\n\n\n\n<p>Por suposto o caso xeral exposto non aplicar\u00e1 a todos os NPC e complicalo moito para ter en conta cada caso espec\u00edfico ser\u00eda moi mal dese\u00f1o.<\/p>\n\n\n\n<p>Un exemplo co que tivemos que lidar foi o dun personaxe que bloqueaba o paso no caso de que o xogador non cumprise unha serie de criterios e se movese cando estes fosen satisfeitos.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"971\" height=\"488\" src=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-13.png\" alt=\"\" class=\"wp-image-98\" srcset=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-13.png 971w, https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-13-300x151.png 300w, https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/04\/image-13-768x386.png 768w\" sizes=\"auto, (max-width: 971px) 100vw, 971px\" \/><\/figure>\n\n\n\n<p>Para isto, unha vez colocado na escena adecuada, activamos a opci\u00f3n &#8220;editable children&#8221; de Godot. Isto permite engadir novos nodos e modificar certas variables dos existentes na escena importada. As\u00ed, creamos o nodo <em>SelectState<\/em> e o definimos como o nodo usado inicialmente na m\u00e1quina de estados.<\/p>\n\n\n\n<p>Este \u00e9 un nodo moi sinxelo que exp\u00f3n d\u00faas variables de tipo <em>State<\/em> e simplemente transiciona a unha ou outra dependendo dunha condici\u00f3n.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Grazas por ler este post! Xunto co anterior, <a href=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/2025\/04\/11\/usos-de-export-en-godot\/\">Usos de @export en Godot<\/a>, recollen a charla que demos na \u00faltima xuntanza <a rel=\"tag\" class=\"hashtag u-tag u-category\" href=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/tag\/godoteires\/\">#godoteires<\/a> en Vigo. Nos post seguintes tratarei de dar algunha nova sobre o que estamos a facer no xogo e non explicaci\u00f3ns tan densas sobre a ferramenta.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Cando estamos a xogar a un videoxogo notamos como hai personaxes ou outros elementos que se comportan de modo distinto segundo o que estea a pasar. Un inimigo pode estar seguindo un cami\u00f1o e correr tras do xogador cando o ve, o propio personaxe do xogador pode subirse a un veh\u00edculo e comezar a conducilo. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":95,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":3,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[1],"tags":[4],"class_list":["post-64","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-sen-categoria","tag-godoteires"],"_links":{"self":[{"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/posts\/64","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/comments?post=64"}],"version-history":[{"count":4,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/posts\/64\/revisions"}],"predecessor-version":[{"id":99,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/posts\/64\/revisions\/99"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/media\/95"}],"wp:attachment":[{"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/media?parent=64"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/categories?post=64"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/tags?post=64"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}