{"id":1204,"date":"2026-02-09T08:00:00","date_gmt":"2026-02-09T07:00:00","guid":{"rendered":"https:\/\/jorgeturiel.es\/?p=1204"},"modified":"2026-02-15T20:22:53","modified_gmt":"2026-02-15T19:22:53","slug":"ghosts-n-goblins-y-cge-parte-7","status":"publish","type":"post","link":"https:\/\/jorgeturiel.es\/?p=1204","title":{"rendered":"Ghosts \u2018n Goblins y CGE. Parte 7"},"content":{"rendered":"\n<p>Crearemos el primer enemigo. Los zombies que aparecen nada m\u00e1s empezar el nivel. Y para crearemos una nueva hoja de sprites y lo implentaremos en una unidad que gestionar\u00e1 su comportamiento.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Creando enemigos<\/h2>\n\n\n\n<p>Crearemos el primer enemigo. Los zombies que aparecen nada m\u00e1s empezar el nivel. Para ello extrae la primera fina del atlas de sprites <em>spritesheet_enemies_level1.<\/em><\/p>\n\n\n\n<p>Dentro de la carpeta, sprites crea una nueva carpeta llamada <em>enemigos_level1<\/em>. Y copia los archivos dentro de ella.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"576\" height=\"327\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen.png\" alt=\"\" class=\"wp-image-1206\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen.png 576w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-300x170.png 300w\" sizes=\"auto, (max-width: 576px) 100vw, 576px\" \/><\/figure>\n<\/div>\n\n\n<p><br>Crea un hoja de sprites en esta misma carpeta.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"680\" height=\"381\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-1.png\" alt=\"\" class=\"wp-image-1207\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-1.png 680w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-1-300x168.png 300w\" sizes=\"auto, (max-width: 680px) 100vw, 680px\" \/><\/figure>\n<\/div>\n\n\n<p>En la nueva hoja de sprites, crea una nueva animaci\u00f3n, pulsando con el bot\u00f3n derecho del rat\u00f3n en la zona de la ventana <em>frames<\/em>, selecciona <em>Add..<\/em><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"997\" height=\"589\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-2.png\" alt=\"\" class=\"wp-image-1208\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-2.png 997w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-2-300x177.png 300w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-2-768x454.png 768w\" sizes=\"auto, (max-width: 997px) 100vw, 997px\" \/><\/figure>\n\n\n\n<p>Selecciona los archivos que contenga al zombie caminando. Y guarda la hoja como <em>zombies.<\/em><\/p>\n\n\n\n<p>Aqu\u00ed tienes una vista de la animaci\u00f3n.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"918\" height=\"528\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-4.png\" alt=\"\" class=\"wp-image-1211\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-4.png 918w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-4-300x173.png 300w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-4-768x442.png 768w\" sizes=\"auto, (max-width: 918px) 100vw, 918px\" \/><\/figure>\n<\/div>\n\n\n<p>Tambi\u00e9n crearemos una animaciones m\u00e1s, que llamaremos <em>salir.<\/em><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"914\" height=\"526\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-5.png\" alt=\"\" class=\"wp-image-1212\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-5.png 914w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-5-300x173.png 300w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-5-768x442.png 768w\" sizes=\"auto, (max-width: 914px) 100vw, 914px\" \/><\/figure>\n<\/div>\n\n\n<p><br>Una vez listo, a\u00f1ade un elemento <em>TcastleScene<\/em>, cambia su nombre a Zombi1, y a\u00f1\u00e1dele el un <em>BoxCollider<\/em> y un <em>RigidBody3<\/em><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"337\" height=\"132\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-15.png\" alt=\"\" class=\"wp-image-1227\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-15.png 337w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-15-300x118.png 300w\" sizes=\"auto, (max-width: 337px) 100vw, 337px\" \/><\/figure>\n<\/div>\n\n\n<p><br>Y en su propiedad <em>Url <\/em>selecciona la hoja de sprites que hemos creado.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"520\" height=\"100\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-16.png\" alt=\"\" class=\"wp-image-1228\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-16.png 520w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-16-300x58.png 300w\" sizes=\"auto, (max-width: 520px) 100vw, 520px\" \/><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Comportamiento<\/h2>\n\n\n\n<p>La idea es que el zombi salga del suelo camine una distancia, y luego se detenga, y se vuelva a enterrar.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Crear la clase TZombieBehavior<\/h3>\n\n\n\n<p>Para ello vamos a crear una nueva unidad, que contendr\u00e1 una clase que desciende de<em> TcastleBehavior.<\/em>En la ventana<em>Files,<\/em> dentro de code vamos a crear una carpeta nueva que llamaremos <em>enemigos<\/em>. Haz click con el bot\u00f3n derecho sobre la ventana que contiene los archivos.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"486\" height=\"367\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-6.png\" alt=\"\" class=\"wp-image-1213\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-6.png 486w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-6-300x227.png 300w\" sizes=\"auto, (max-width: 486px) 100vw, 486px\" \/><\/figure>\n<\/div>\n\n\n<p>Dentro de este directorio, vuelve a pulsar con el bot\u00f3n derecho del rat\u00f3n, y crearemos una nueva unidad (<em>New Unit here)<\/em> clase de tipo <em>Behavior.<\/em><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"633\" height=\"367\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-7.png\" alt=\"\" class=\"wp-image-1214\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-7.png 633w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-7-300x174.png 300w\" sizes=\"auto, (max-width: 633px) 100vw, 633px\" \/><\/figure>\n<\/div>\n\n\n<p>En el asistente que aparece, pon el nombre <em>Zombies,<\/em> en el campo <em>Base Nane, <\/em>y pulsa <em>Ok<\/em><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"618\" height=\"460\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-8.png\" alt=\"\" class=\"wp-image-1215\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-8.png 618w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-8-300x223.png 300w\" sizes=\"auto, (max-width: 618px) 100vw, 618px\" \/><\/figure>\n<\/div>\n\n\n<p>Al pulsar <em>Ok,<\/em> saldr\u00e1 un aviso como el siguiente.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"472\" height=\"201\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-9.png\" alt=\"\" class=\"wp-image-1216\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-9.png 472w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-9-300x128.png 300w\" sizes=\"auto, (max-width: 472px) 100vw, 472px\" \/><\/figure>\n<\/div>\n\n\n<p>El motor gr\u00e1fico est\u00e1 avisando que el nuevo archivo no est\u00e1 en la ruta del compilador. Esto significa que al compilar el programa, esta unidad no se podr\u00e1 encontrar, y si el c\u00f3digo en alguna parte hace referencia a esta unida, se producir\u00e1 un error. Adem\u00e1s nos indica como podemos solucionarlo.<\/p>\n\n\n\n<p>Al pulsar <em>Aceptar<\/em>, sale un nuevo aviso indicando preguntando si deseamos empezar a editar (introducir c\u00f3digo) en esta nueva unidad. Pulsa <em>s\u00ed.<\/em><\/p>\n\n\n\n<p>Se abrir\u00e1 el editor por defecto, y aparecer\u00e1 la unidad. En el caso de <em>Lazarus<\/em>, lo primero que debemos hacer es a\u00f1adir esta unidad al proyecto. Selecciona en el men\u00fa P<em>royecto, A\u00f1adir Archivo de editor al proyect<\/em><em>o<\/em>, o el atajo de teclado <em>Shift+F1.<\/em><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"389\" height=\"697\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-10.png\" alt=\"\" class=\"wp-image-1218\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-10.png 389w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-10-167x300.png 167w\" sizes=\"auto, (max-width: 389px) 100vw, 389px\" \/><\/figure>\n<\/div>\n\n\n<p>Y pulsa <em>S\u00ed, <\/em>sobre la ventana de confirmaci\u00f3n.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"467\" height=\"163\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-11.png\" alt=\"\" class=\"wp-image-1219\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-11.png 467w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-11-300x105.png 300w\" sizes=\"auto, (max-width: 467px) 100vw, 467px\" \/><\/figure>\n<\/div>\n\n\n<p>De esta manera, <em>Lazarus,<\/em> reconoce la unidad como parte de proyecto. Ahora solo queda indicar a <em>CGE<\/em>, que reconozca la unidad. Vuelve al editor, y selecciona el origen del del \u00e1rbol de archivos, y localiza el archivo <em>CastleEngineManifiest.xml<\/em>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"476\" height=\"303\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-12.png\" alt=\"\" class=\"wp-image-1220\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-12.png 476w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-12-300x191.png 300w\" sizes=\"auto, (max-width: 476px) 100vw, 476px\" \/><\/figure>\n<\/div>\n\n\n<p>Este es el archivo d\u00f3nde debemos indicar la ruta, d\u00f3nde CGE debe localizar el archivo nuevo. Abre la carpeta contenedora, pulsando con el bot\u00f3n derecho del rat\u00f3n y seleccionando <em>Open Containing Folde<\/em>r.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"476\" height=\"303\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-13.png\" alt=\"\" class=\"wp-image-1221\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-13.png 476w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-13-300x191.png 300w\" sizes=\"auto, (max-width: 476px) 100vw, 476px\" \/><\/figure>\n<\/div>\n\n\n<p>En el caso que este usando <em>Visual Code<\/em>, como editor podr\u00e1s abrir este archivo directamente.<br>Edita el archivo, a\u00f1ade una entrada nueva dentro de <em>compiler_options. <\/em>Indicando la nueva ruta. F\u00edjate en la imagen siguiente.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"471\" height=\"281\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-14.png\" alt=\"\" class=\"wp-image-1222\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-14.png 471w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/imagen-14-300x179.png 300w\" sizes=\"auto, (max-width: 471px) 100vw, 471px\" \/><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Programando la clase TzombieBehavior<\/h2>\n\n\n\n<p>Para mover el zombie una distancia determinada necesitamos tres datos: velocidad, direcci\u00f3n y distancia m\u00e1xima. Abre el editor de c\u00f3digo, y selecciona el archivo <em>GameZombiesBehavior,<\/em> sino lo tienes a la vista.<\/p>\n\n\n\n<p>Para ello vamos a crear en la secci\u00f3n <em>public<\/em> las tres propiedades.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \"> public\n    property Direccion: TVector3 read Fdireccion write Setdireccion;\n    property Velocidad: TVector3 read Fvelocidad write Setvelocidad;\n    property Distancia: single read FDistancia write SetDistancia; <\/pre><\/div>\n\n\n\n<p>F\u00edjate que hay dos procedimientos ya declarados, que son estos:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">public\n    procedure ParentAfterAttach; override;\n    procedure Update(const SecondsPassed: single; var RemoveMe: TRemoveType); override;   <\/pre><\/div>\n\n\n\n<p>El procedimiento <em>Update<\/em> es el que se llamar\u00e1 cada vez que se vaya a actualizar el elemento <em>TcastleScene<\/em> al que pertenece esta clase. Ten en cuenta que en esta unidad estamos declarando una clase comportamiento o <em>Tbehaivor <\/em>el cual a\u00f1adirenos a un elemento <em>TcastleScene<\/em>.<\/p>\n\n\n\n<p>El otro procedimiento, se ejecutar\u00e1 justo cuando se a\u00f1ade esta clase al elemento <em>TcastleScene<\/em>. Dentro de este procedimiento escribiremos lo siguiente:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">procedure TZombieBehavior.ParentAfterAttach;\nbegin\n  inherited;\n  { Parent is available now. }\n  FOrigen := Parent.Translation;\n  FRigidBody := Parent.RigidBody;\n  FRigidBody.LinearVelocity := Vector3(0, 0, 0);\n  AnimacionSalir;\nend; <\/pre><\/div>\n\n\n\n<p>Tal como indica el comentario, la clase <em>Parent<\/em>, ya est\u00e1 disponible, por lo tanto aprovechanos para asginar valores a las propiedades que hemos creado antes. Y llamar al procedimiento <em>Animaci\u00f3nSalir<\/em>, d\u00f3nde vamos a programar la animaci\u00f3n que se realiza al principio.<\/p>\n\n\n\n<p>En este procedimiento preparamos la animaci\u00f3n<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">procedure TZombieBehavior.AnimacionSalir;\nvar\n  Parameters: TPlayAnimationParameters;\nbegin\n  FPosicionActual := Parent.Translation;\n  Parameters := TPlayAnimationParameters.Create;\n  try\n    Parameters.Loop := False;\n    Parameters.Name := 'salir';\n    Parameters.Forward := True;\n    Parameters.StopNotification :=\n    {$IFDEF FPC}\n       @FinAnimacionSalir;\n      {$ELSE]}\n      FinAnimacionSalir;\n    {$ENDIF}\n    TCastleScene(Parent).PlayAnimation(Parameters);\n  finally\n    FreeAndNil(Parameters);\n  end;\nend;    <\/pre><\/div>\n\n\n\n<p>Primero obtenemos la posici\u00f3n actual. El resto del procedimiento, es la creaci\u00f3n de la animaci\u00f3n.<\/p>\n\n\n\n<p>Cuando la animaci\u00f3n termina, llamamos se llama al procedimiento <em>FinAnimacionSalir<\/em>. El cual contiene el siguiente c\u00f3digo<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">procedure TZombieBehavior.FinAnimacionSalir(const Scene: TCastleSceneCore;\n  const Animation: TTimeSensorNode);\nvar\n  Posicion: TVector3;\nbegin\n  TCastleScene(Parent).PlayAnimation('walk', True);\n  FRigidBody.LinearVelocity := FVelocidad * FDireccion;\n  Posicion := Parent.Translation;\n  Posicion.Y := FOrigen.y;\n  Parent.Translation := Posicion;\nend;   \n<\/pre><\/div>\n\n\n\n<p><br>En este procedimiento, lo primero, es indicar que la animaci\u00f3n que se ejecute la animaci\u00f3n <em>walk<\/em>, de manera continua. Le asignamos una velocidad y una direcci\u00f3n. Estos datos son los que hemos guardado anteriormente. Nos aseguramos que la coordenada Y del padre, es la original. Ya que como veremos m\u00e1s adelante, en el evento <em>OnUpdate<\/em>, la coordenada Y es modificada.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Evento OnUpdate. El centro de todo<\/h3>\n\n\n\n<p>Tal como hemos visto anteriomente, el evento <em>OnUpdate<\/em>, en el que se ejecuta repetidamente y d\u00f3nde gestionamos todo.<\/p>\n\n\n\n<p>En este caso, el zombie, tiene dos animaciones. Una <em>walk<\/em>, que es cuando camina, y la otra es <em>salir<\/em>. Adem\u00e1s el zombie se puede enterrar, lo cual es la animaci\u00f3n <em>salir<\/em>, pero ejecutada hacia atr\u00e1s.<\/p>\n\n\n\n<p>Teniendo eso en cuenta, en el evento <em>OnUpdate<\/em> debemos gestionar las animaciones. El c\u00f3digo no es muy largo, pero si efectivo<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">procedure TZombieBehavior.Update(const SecondsPassed: single; var RemoveMe: TRemoveType);\nvar\n  TiempoActual: TFloatTime;\n  AnimationNode: TTimeSensorNode;\n  FrameActual: integer;\n  Posicion: TVector3;\nbegin\n  inherited;\n\n  AnimationNode := TCastleScene(Parent).CurrentAnimation;\n  if AnimationNode &lt;&gt; nil then\n  begin\n    if AnimationNode.X3DName = 'walk' then\n    begin\n      FRigidBody.LinearVelocity := FVelocidad * FDireccion;\n      if (PointsDistance(FOrigen, Parent.Translation) &gt; 100) then\n      begin\n        FRigidBody.LinearVelocity := Vector3(0, 0, 0);\n        \/\/Pasar a la animaci\u00f3n de enterrarse\n        AnimacionEnterrar;\n      end;\n    end;\n    if AnimationNode.X3DName = 'salir' then\n    begin\n      \/\/ FractionIncreasing corresponde a la animaci\u00f3n hacia adelante\n      if AnimationNode.FractionIncreasing then\n      begin\n        FrameActual := Floor((AnimationNode.ElapsedTimeInCycle \/\n          AnimationNode.CycleInterval) * 7);\n      end\n      else\n      begin\n        TiempoActual := AnimationNode.CycleInterval -\n          AnimationNode.ElapsedTimeInCycle;\n        FrameActual := Floor((TiempoActual \/ AnimationNode.CycleInterval) * 7);\n      end;\n      Posicion := TCastleScene(Parent).Translation;\n      case FrameActual of\n        0:\n          Posicion.Y := FOrigen.y - 27;\n        1:\n          Posicion.Y := FOrigen.y - 25;\n        2:\n          Posicion.Y := FOrigen.y - 24;\n        3:\n          Posicion.Y := FOrigen.y - 20;\n        4:\n          Posicion.Y := FOrigen.y - 17;\n        5:\n          Posicion.Y := FOrigen.y - 8;\n        else\n          Posicion.Y := FOrigen.y;\n      end;\n      Parent.Translation := Posicion;\n    end;\n  end;\n\nend;                        \n<\/pre><\/div>\n\n\n\n<p>Lo primero es aseguranos que hay alguna animaci\u00f3n activa, algo que siempre deber\u00eda ser as\u00ed pero no est\u00e1 dem\u00e1s.<\/p>\n\n\n\n<p>Si esta animaci\u00f3n es <em>walk<\/em>, nos aseguremos que el zombie tiene una velocidad, asign\u00e1ndole una. Lo siguiente es comprobar si ha recorrido una distancia. Para ello calculamos la distancia entre el origen y la posici\u00f3n actual. Para ello nos ayudamos de la funci\u00f3n <em>PointsDistance<\/em>, la cual nos da la distancia entre dos puntos. Si esta distancia es mayor de 100, se considera que el zombie debe enterrarse. Por tanto llamamos al procedimiento <em>AnimacionEnterrar<\/em>.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">procedure TZombieBehavior.AnimacionEnterrar;\nvar\n  Parameters: TPlayAnimationParameters;\n  FPosicionActual := Parent.Translation;\n  Parameters := TPlayAnimationParameters.Create;\n  try\n    Parameters.Loop := False;\n    Parameters.Name := 'salir';\n    \/\/Realizamos la animaci\u00f3n de salir hacia atr\u00e1s\n    Parameters.Forward := False;\n    Parameters.StopNotification :=\n    {$IFDEF FPC}\n       @FinAnimacionEnterrar;\n      {$ELSE]}\n      FinAnimacionEnterrar;\n    {$ENDIF}\n    TCastleScene(Parent).PlayAnimation(Parameters);\n  finally\n    FreeAndNil(Parameters);\n  end;\nend; \n<\/pre><\/div>\n\n\n\n<p>De nuevo este procedimiento es muy parecido a <em>FinAnimacionSalir<\/em>. La diferencia est\u00e1 en que se oculta el zombie, se restaura su posici\u00f3n al origen, se vuelve a hacer visible, y llamamos a la animaci\u00f3n <em>AnimacionSalir<\/em>.<\/p>\n\n\n\n<p>Continuando con el evento <em>OnUpdate, <\/em>con la animaci\u00f3n actual es <em>salir. <\/em>Comprobamos si se est\u00e1 ejecutando hacia adelante o hacia atr\u00e1s. Dependiendo de ello, lo que hacemos en calcular que frame de la animaci\u00f3n se est\u00e1 ejecutando, de esta manera en funci\u00f3n del frame actual, iremos cambiando su posici\u00f3n vertical (coordenada Y) para conseguir un efecto de movimiento vertical.<\/p>\n\n\n\n<p>Para calcular el frame actual, debemos obtener el tiempo que lleva la animaci\u00f3n ejecut\u00e1ndose, divirlo por el tiempo total de la animaci\u00f3n y multiplicarlo con el n\u00famero de animaciones. El motico por el cual debemos saber en que sentido se est\u00e1 ejecutando la animaci\u00f3n, es que la forma de obtener el tiempo actual varia.<\/p>\n\n\n\n<p>Con esto tenemos el comportamiento del zombi agrupado en una sola unidad, la cual se puede reusar para m\u00faltiples zombies.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><br>A\u00f1adir el comportamiento al zombie<\/h2>\n\n\n\n<p><br>En la unidad principal, a\u00f1ade en la secci\u00f3n principal el zombi (<em>TcastleScene)<\/em> que creamos antes.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"649\" height=\"112\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/image.png\" alt=\"\" class=\"wp-image-1230\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/image.png 649w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/image-300x52.png 300w\" sizes=\"auto, (max-width: 649px) 100vw, 649px\" \/><\/figure>\n<\/div>\n\n\n<p>Y en la secci\u00f3n <em>published<\/em> a\u00f1ade el <em>TcaslteScene<\/em> llamado zombie1<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"295\" height=\"205\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2026\/02\/image-1.png\" alt=\"\" class=\"wp-image-1231\"\/><\/figure>\n<\/div>\n\n\n<p><br>Y ahora en el el procedimiento a\u00f1ade este c\u00f3digo<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">\/\/Crear zombi\n  ZombieBehavior := TZombieBehavior.Create(FreeAtStop);\n  ZombieBehavior.Velocidad:=Vector3(10,0,0);\n  ZombieBehavior.Direccion:=Vector3(-1,0,0);\n  Zombi1.AddBehavior(ZombieBehavior);\n  ZombieBehavior.ParentAfterAttach;  <\/pre><\/div>\n\n\n\n<p>Creamos un una clase <em>TzombieBehavior<\/em>. Le asignamos un vector que indicara su velocidad, y hacemos lo mismo para la direcci\u00f3n. Y a\u00f1adimos este comportamiento al zombi de tipo <em>TcastleScene<\/em>. Ya por \u00faltimo llamar al m\u00e9todo <em>ParentAfterAttach.<\/em><\/p>\n\n\n\n<p>Y ya solo queda ejecutar nuestro programa para ver como el zombie, sale del suelo avanza una distancia, y se vuelve a esconder, para reaparecer de nuevo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusiones<\/h2>\n\n\n\n<p>En esta parte, hemos visto como crear unidades y a\u00f1adirlas. As\u00ed como crear un comportamiento para asignarlo a un TCastleScene. <\/p>\n\n\n\n<p>Tienes el c\u00f3digo disponible en el <a href=\"https:\/\/github.com\/Blueicaro\/GhostAndGoblins_CGE-.git\">github<\/a>.<\/p>\n\n\n\n<p>Saludos<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Crearemos el primer enemigo. Los zombies que aparecen nada m\u00e1s empezar el nivel. Y para crearemos una nueva hoja de sprites y lo implentaremos en una unidad que gestionar\u00e1 su comportamiento.<\/p>\n","protected":false},"author":2,"featured_media":1232,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[57,56,61,48],"tags":[49,32,23,21,24],"class_list":["post-1204","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-castle-game-engine","category-cge","category-free-pascal","category-videojuegos","tag-castle-game-engine","tag-free-pascal","tag-lazarus","tag-pascal","tag-programacion"],"_links":{"self":[{"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/posts\/1204","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1204"}],"version-history":[{"count":10,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/posts\/1204\/revisions"}],"predecessor-version":[{"id":1234,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/posts\/1204\/revisions\/1234"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/media\/1232"}],"wp:attachment":[{"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1204"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1204"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1204"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}