{"id":899,"date":"2024-10-07T00:00:00","date_gmt":"2024-10-06T23:00:00","guid":{"rendered":"https:\/\/jorgeturiel.es\/?p=899"},"modified":"2024-11-24T20:55:16","modified_gmt":"2024-11-24T19:55:16","slug":"telepong-con-castle-game-engine-parte-2","status":"publish","type":"post","link":"https:\/\/jorgeturiel.es\/?p=899","title":{"rendered":"TelePong con Castle Game Engine. Parte 2"},"content":{"rendered":"\n<p>Continuamos recreando el m\u00edtico juego de Atari Telepong con Castle Game Engine. En la entrada anterior preparamos todo el entorno usando el editor. En esta entrada vamos a programar algunas l\u00edneas de c\u00f3digo. Vamos a ello.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Moviendo los jugadores<\/h2>\n\n\n\n<p>Lo primero que programaremos es el movimiento de los jugadores. Y se producir\u00e1 al pulsar unas teclas. Por ejemplo, la tecla Q y A servir\u00e1n para desplazar el jugador uno, arriba y abajo. Y las teclas de los cursores, arriba y abajo har\u00e1n la misma funci\u00f3n pero para el jugador 2. <\/p>\n\n\n\n<p>Abre el editor de c\u00f3digo pulsando <em>F12. <\/em>En la clausula <em>uses<\/em> a\u00f1ade la unidad <em>CastleScene<\/em>, y a\u00f1ade los jugadores en la parte <em>published<\/em>.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">uses Classes,\n  CastleVectors, CastleComponentSerialize,\n  CastleUIControls, CastleControls, CastleKeysMouse,CastleScene;\n\ntype\n  { Main view, where most of the application logic takes place. }\n  TViewMain = class(TCastleView)\n  published\n    { Components designed using CGE editor.\n      These fields will be automatically initialized at Start. }\n    LabelFps: TCastleLabel;\n    Jugador1 : TCastleBox;\n    Jugador2 : TCastleBox;  <\/pre><\/div>\n\n\n\n<p>Ahora en la funci\u00f3n <em>Press<\/em>, gestionaremos las teclas. Cuando se pulse una tecla de las que hemos definido anteriormente, aplicaremos una velocidad en el sentido que corresponda en el caso que no tenga velocidad el jugador. Si el velocidad es en sentido contrario al pulsado la velocidad aplicada ser\u00e1 cero. Con ello se detendr\u00e1 el jugador. Define en la secci\u00f3n <em>Private<\/em> dos variables que contendr\u00e1n la velocidad de cada jugador<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">type\n  { Main view, where most of the application logic takes place. }\n  TViewMain = class(TCastleView)\n  private\n    VelocidadPlayer1: integer;\n    VelocidadPlayer2: integer;\n                                <\/pre><\/div>\n\n\n\n<p>En el procedimiento <em>start<\/em>, inicializa las variables a cero. El procedimiento <em>start<\/em> se ejecuta una sola vez, cuando se inicia nuestro programa. Por ello es un buen lugar para inicializar las variables, clases, etc.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">procedure TViewMain.Start;\nbegin\n  inherited;\n  VelocidadPlayer1 := 0;\n  VelocidadPlayer2 := 0;\nend;    <\/pre><\/div>\n\n\n\n<p>El evento <em>Press<\/em> recoge las pulsaciones de las teclas. As\u00ed, que tal como comentamos anteriormente, capturamos la pulsaci\u00f3n de las teclas, y asignamos un valor a la velocidad del jugador.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">function TViewMain.Press(const Event: TInputPressRelease): boolean;\nbegin\n  Result := inherited;\n  if Result then Exit; \/\/ allow the ancestor to handle keys\n\n  if Event.IsKey(keyQ) then\n  begin\n    if VelocidadPlayer1 &lt; 0 then\n    begin\n      VelocidadPlayer1 := 0;\n    end\n    else\n    begin\n      VelocidadPlayer1 := 300;\n    end;\n    Exit(True);\n  end;\n  if Event.IsKey(keyA) then\n  begin\n    if VelocidadPlayer1 &gt; 0 then\n    begin\n      VelocidadPlayer1 := 0;\n    end\n    else\n    begin\n      VelocidadPlayer1 := -300;\n    end;\n  end;\n  \/\/Teclas jugadaor 2\n  if Event.IsKey(keyArrowUp) then\n  begin\n    if VelocidadPlayer2 &lt; 0 then\n    begin\n      VelocidadPlayer2 := 0;\n    end\n    else\n    begin\n      VelocidadPlayer2 := 300;\n    end;\n    Exit(True);\n  end;\n  if Event.IsKey(keyArrowDown) then\n  begin\n    if VelocidadPlayer2 &gt; 0 then\n    begin\n      VelocidadPlayer2 := 0;\n    end\n    else\n    begin\n      VelocidadPlayer2 := -300;\n    end;\n    Exit(True);\n  end;\nend;\n                             <\/pre><\/div>\n\n\n\n<p>Hasta ahora solo hemos capturado las teclas y asignado valores a las variables de la velocidad. Para que se muevan los jugadores debemos trasladarlos en el eje Y, cada vez que se actualice el juego, osea en cada frame. <\/p>\n\n\n\n<p>El evento <em>update<\/em> se encarga de esto. As\u00ed que cambiaremos la posici\u00f3n de los jugadores (trasladar) , en funci\u00f3n del valor de la variable multiplicada por una constante <em>SecondPassed<\/em>. Al multiplicar por esta constante conseguimos que la velocidad de desplazamiento sea la misma, con independencia del rendimiento del equipo d\u00f3nde se est\u00e9 ejecutando el juego.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">procedure TViewMain.Update(const SecondsPassed: single; var HandleInput: boolean);\nbegin\n  inherited;\n  { This virtual method is executed every frame (many times per second). }\n  Assert(LabelFps &lt;&gt; nil,\n    'If you remove LabelFps from the design, remember to remove also the assignment \"LabelFps.Caption := ...\" from code');\n  LabelFps.Caption := 'FPS: ' + Container.Fps.ToString;\n\n  if (Player1.Translation.Y &gt; 300) then\n  begin\n    VelocidadPlayer1 := 0;\n    Player1.Translation := Vector3(-690, 300, 0);\n  end;\n\n  if (Player1.Translation.Y &lt; -300) then\n  begin\n    VelocidadPlayer1 := 0;\n    Player1.Translation := Vector3(-690, -300, 0);\n  end;\n\n\n  if (Player2.Translation.Y &gt; 300) then\n  begin\n    VelocidadPlayer2 := 0;\n    Player2.Translation := Vector3(690, 300, 0);\n  end;\n\n  if (Player2.Translation.Y &lt; -300) then\n  begin\n    VelocidadPlayer2 := 0;\n    Player2.Translation := Vector3(690, -300, 0);\n  end;\n\n\n\n  Player1.Translation := Player1.Translation +\n    Vector3(0, VelocidadPlayer1 * SecondsPassed, 0);\n  Player2.Translation := Player2.Translation +\n    Vector3(0, VelocidadPlayer2 * SecondsPassed, 0);\nend;     <\/pre><\/div>\n\n\n\n<p>Adem\u00e1s, debemos limitar la posici\u00f3n del jugador para que no se salga de los l\u00edmites de campo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Gestionando las colisiones.<\/h2>\n\n\n\n<p>Ya solo nos queda gestionar cuando la pelota colisione con uno de los lados, con ello sabremos que un jugador ha marcado un gol. <\/p>\n\n\n\n<p>A\u00f1ade en la secci\u00f3n <em>published<\/em> los elemento <em>LadoDerecho <\/em> y <em>LadoIzquierdo<\/em>.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \"> published\n    { Components designed using CGE editor.\n      These fields will be automatically initialized at Start. }\n    LabelFps: TCastleLabel;\n    Player1: TCastleBox;\n    Player2: TCastleBox;\n    LadoDerecho: TCastleBox;\n    LadoIzquierdo: TCastleBox;\n  public               <\/pre><\/div>\n\n\n\n<p>En el evento <em>Start<\/em>, asignaremos a cada <em>RigidBody<\/em> de cada uno de los lados un procedimiento que se ejecutar\u00e1 cada vez que se produzca un colisi\u00f3n (<em>OnCollisionEnter<\/em>). <\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">procedure TViewMain.Start;\nvar\n  Body: TCastleRigidBody;\nbegin\n  inherited;\n  VelocidadPlayer1 := 0;\n  VelocidadPlayer2 := 0;\n\n  \/\/Lado Derecho\n  Body := LadoDerecho.FindBehavior(TCastleRigidBody) as TCastleRigidBody;\n  {$IFDEF FPC}\n  Body.OnCollisionEnter := @ColisionLadoDerecho;\n  {$ELSE}\n  Body.OnCollisionEnter := ColisionLadoDerecho;\n  {$ENDIF}\n\n  \/\/Lado Izqquierdo\n  Body := LadoIzquierdo.FindBehavior(TCastleRigidBody) as TCastleRigidBody;\n  {$IFDEF FPC}\n  Body.OnCollisionEnter := @ColisionLadoIzquierdo;\n  {$ELSE}\n  Body.OnCollisionEnter := ColisionLadoIzquierdo;\n  {$ENDIF}\n\nend;<\/pre><\/div>\n\n\n\n<p>A\u00f1ad\u00ed un condicional de compilaci\u00f3n,  de tal manera que si este c\u00f3digo se compila con Free Pascal, la asignaci\u00f3n del evento lleva la letra @ delante. En caso contrario,  que es cuando se compila con <a href=\"https:\/\/www.embarcadero.com\/es\/products\/delphi\">Delphi<\/a>, no lo lleva. Con esto conseguimos tener un c\u00f3digo portable o <em>crosscompile<\/em>.<\/p>\n\n\n\n<p>En Lazarus, si pulsas la combinaci\u00f3n de teclas <em>ctrl+c,<\/em> en el sobre la l\u00ednea en la que que se asigna el evento, por ejemplo la l\u00ednea 12, el editor crear\u00e1 autom\u00e1ticamente el procedimiento.<\/p>\n\n\n\n<p>Cada vez que se produzca una colisi\u00f3n, anotaremos un punto al jugador correspondiente. As\u00ed si la bola colisiona con el lado derecho, el punto ser\u00e1 para jugador uno. Y actualizaremos la etiqueta de este jugador.<\/p>\n\n\n\n<p>En la secci\u00f3n privada a\u00f1ade dos variables de tipo entero, para guardar la puntuaci\u00f3n de cada jugado. En la secci\u00f3n <em>published <\/em>declara los elementos <em>TCastleLabel<\/em>, que representan el marcador. Algo as\u00ed:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \" >TViewMain = class(TCastleView)\n  private\n    VelocidadPlayer1: integer;\n    VelocidadPlayer2: integer;\n    PuntosPlayer1: integer; \/\/A\u00f1ade esto\n    PuntosPlayer2: integer; \/\/A\u00f1ade esto\n    procedure ColisionLadoDerecho(const CollisionDetails: TPhysicsCollisionDetails);\n    procedure ColisionLadoIzquierdo(\n      const CollisionDetails: TPhysicsCollisionDetails);\n    procedure ColisionPlayer1(const CollisionDetails: TPhysicsCollisionDetails);\n  published\n    { Components designed using CGE editor.\n      These fields will be automatically initialized at Start. }\n    LabelFps: TCastleLabel;\n    Player1: TCastleBox;\n    Player2: TCastleBox;\n    LadoDerecho: TCastleBox;\n    LadoIzquierdo: TCastleBox;\n    MarcadorPlayer1: TCastleLabel; \/\/A\u00f1ade esto\n    MarcadorPlayer2: TCastleLabel; \/\/A\u00f1ade esto           <\/pre><\/div>\n\n\n\n<p>En los eventos de colisi\u00f3n que creamos anteriormente a\u00f1ade el c\u00f3digo para incrementar los puntos de cada jugador.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \" >procedure TViewMain.ColisionLadoDerecho(\n  const CollisionDetails: TPhysicsCollisionDetails);\nbegin\n  \/\/EL jugador 1  (izquierdo) consigue un punto\n  PuntosPlayer1 := PuntosPlayer1 + 1;\n  MarcadorPlayer1.Caption := IntToStr(PuntosPlayer1);\nend;\n\nprocedure TViewMain.ColisionLadoIzquierdo(\n  const CollisionDetails: TPhysicsCollisionDetails);\nbegin\n  \/\/El jugador 2 (derecha) consigue un punto\n  PuntosPlayer2 := PuntosPlayer2 + 1;\n  MarcadorPlayer2.Caption := IntToStr(PuntosPlayer2);\nend;    <\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Probando<\/h2>\n\n\n\n<p>Antes de probar el juego, vete al editor de CGE, y cambia la propiedad <em>Visible<\/em> de los elementos <em>LadoDerecho<\/em> y <em>LadoIzquierdo<\/em> de su valor <em>True<\/em> a <em>False<\/em>. Con ello, ocultamos los lados de la pista.<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"492\" height=\"691\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2024\/09\/Captura-de-pantalla-2024-10-06-001052.png\" alt=\"\" class=\"wp-image-915\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2024\/09\/Captura-de-pantalla-2024-10-06-001052.png 492w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2024\/09\/Captura-de-pantalla-2024-10-06-001052-214x300.png 214w\" sizes=\"auto, (max-width: 492px) 100vw, 492px\" \/><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"492\" height=\"662\" src=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2024\/09\/Captura-de-pantalla-2024-10-06-001109.png\" alt=\"\" class=\"wp-image-916\" srcset=\"https:\/\/jorgeturiel.es\/wp-content\/uploads\/2024\/09\/Captura-de-pantalla-2024-10-06-001109.png 492w, https:\/\/jorgeturiel.es\/wp-content\/uploads\/2024\/09\/Captura-de-pantalla-2024-10-06-001109-223x300.png 223w\" sizes=\"auto, (max-width: 492px) 100vw, 492px\" \/><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p>Ahora, compila el juego desde el editor y observa el resultado.<\/p>\n\n\n\n<p>Ya tenemos un juego casi funcional, aunque le quedan algunos detalles para ser totalmente funcional, como el sonido, detener el juego cuando se alcance un n\u00famero de puntos, reiniciarlo, etc. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusiones<\/h2>\n\n\n\n<p>En esta parte hemos visto:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Hacer uso de los elemento creados en el editor desde c\u00f3digo.<\/li>\n\n\n\n<li>Como desplazar un elemento en el evento <em>Updat<\/em>e.<\/li>\n\n\n\n<li>Como gestionar las teclas en el evento <em>Press<\/em>.<\/li>\n\n\n\n<li>Gestionar los eventos de colisi\u00f3n.<\/li>\n<\/ul>\n\n\n\n<p>Saludos<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Continuamos recreando el m\u00edtico juego de Atari Telepong con Castle Game Engine. En la entrada anterior preparamos todo el entorno usando el editor. En esta entrada vamos a programar algunas l\u00edneas de c\u00f3digo. Vamos a ello.<\/p>\n","protected":false},"author":2,"featured_media":893,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[57,27,25,48],"tags":[49,23,21,24],"class_list":["post-899","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-castle-game-engine","category-lazarus","category-pascal","category-videojuegos","tag-castle-game-engine","tag-lazarus","tag-pascal","tag-programacion"],"_links":{"self":[{"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/posts\/899","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=899"}],"version-history":[{"count":12,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/posts\/899\/revisions"}],"predecessor-version":[{"id":917,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/posts\/899\/revisions\/917"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/media\/893"}],"wp:attachment":[{"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=899"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=899"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=899"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}