{"id":407,"date":"2015-08-19T12:24:18","date_gmt":"2015-08-19T11:24:18","guid":{"rendered":"http:\/\/www.pixnbgames.com\/blog\/?p=407"},"modified":"2015-08-19T19:58:24","modified_gmt":"2015-08-19T18:58:24","slug":"animaciones-cuadro-por-cuadro-en-libgdx","status":"publish","type":"post","link":"https:\/\/www.pixnbgames.com\/blog\/libgdx\/animaciones-cuadro-por-cuadro-en-libgdx\/","title":{"rendered":"Animaciones cuadro por cuadro en LibGDX"},"content":{"rendered":"<h2 style=\"text-align: justify;\">Introducci\u00f3n<\/h2>\n<p style=\"text-align: justify;\">A la hora de animar los elementos\u00a0de nuestros videojuegos, uno de los\u00a0m\u00e9todos con los que contamos son las animaciones cuadro por cuadro (frame by frame).\u00a0Es el m\u00e9todo cl\u00e1sico de animaci\u00f3n, y consiste en dibujar una serie de im\u00e1genes de forma que, al mostrarlas de forma secuencial, dan la sensaci\u00f3n de que el elemento realiza alguna acci\u00f3n.<\/p>\n<p style=\"text-align: justify;\"><a href=\"http:\/\/www.pixnbgames.com\/blog\/wp-content\/uploads\/2015\/08\/runner.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-428\" src=\"http:\/\/www.pixnbgames.com\/blog\/wp-content\/uploads\/2015\/08\/runner.gif\" alt=\"runner\" width=\"80\" height=\"120\" \/><\/a>Por ejemplo, si queremos mostrar un personaje corriendo, deber\u00edamos tener\u00a0una serie de im\u00e1genes, cada una con peque\u00f1o avance sobre la imagen anterior; de forma que al mostrarlas una detr\u00e1s de otra, podamos ver como el personaje se mueve:<\/p>\n<p style=\"text-align: justify;\"><a href=\"http:\/\/www.pixnbgames.com\/blog\/wp-content\/uploads\/2015\/08\/charset.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-424\" src=\"http:\/\/www.pixnbgames.com\/blog\/wp-content\/uploads\/2015\/08\/charset.png\" alt=\"charset\" width=\"559\" height=\"240\" srcset=\"https:\/\/www.pixnbgames.com\/blog\/wp-content\/uploads\/2015\/08\/charset.png 559w, https:\/\/www.pixnbgames.com\/blog\/wp-content\/uploads\/2015\/08\/charset-300x129.png 300w\" sizes=\"(max-width: 559px) 100vw, 559px\" \/><\/a><\/p>\n<p>&nbsp;<br \/>\n&nbsp;<\/p>\n<h1 style=\"text-align: justify;\">Animaciones cuadro por cuadro en LibGDX<\/h1>\n<h2>La clase Animation<\/h2>\n<p style=\"text-align: justify;\">La clase encargada de gestionar nuestras animaciones cuadro por cuadro es <code>Animation<\/code>. El trabajo de esta clase consiste en calcular el frame (cuadro) que debe pintarse en cada momento, siendo nosotros los encargados finales de dibujar el resultado.<\/p>\n<p style=\"text-align: justify;\">B\u00e1sicamente esta clase necesita conocer estos cuatro elementos:<\/p>\n<ul>\n<li style=\"text-align: justify;\"><strong>Im\u00e1genes que componen la animaci\u00f3n.<\/strong> Cada una de estas im\u00e1genes es un cuadro de nuestra animaci\u00f3n.<\/li>\n<li style=\"text-align: justify;\"><strong>Tiempo que debe transcurrir entre un cuadro y el siguiente.<\/strong><\/li>\n<li style=\"text-align: justify;\"><strong>Modo de animaci\u00f3n.<\/strong> Est\u00e1n definidos en el enumerado <code>Animation.PlayMode<\/code>, e indica a nuestro objeto <code>Animation<\/code> qu\u00e9 orden deben seguir las im\u00e1genes para conseguir la animaci\u00f3n deseada. Los modos posibles son:\n<ul>\n<li style=\"text-align: justify;\"><code>NORMAL<\/code>: Los cuadros se dibujan del primero al \u00faltimo. Sin repetici\u00f3n. Es decir, una vez se alcance el \u00faltimo cuadro, siempre se dibujar\u00e1 este \u00faltimo cuadro.<\/li>\n<li style=\"text-align: justify;\"><code>REVERSED<\/code>: Los cuadros se dibujan del \u00faltimo al primero. Sin repetici\u00f3n.<\/li>\n<li style=\"text-align: justify;\"><code>LOOP<\/code>: Los cuadros se dibujan del primero al \u00faltimo. Una vez llegado al \u00faltimo cuadro, se vuelve a empezar por el primero y as\u00ed sucesivamente.<\/li>\n<li style=\"text-align: justify;\"><code>LOOP_REVERSED<\/code>: Los cuadros se dibujan del \u00faltimo al primero. Una vez alcanzado el primer cuadro, se vuelve a comenzar por el \u00faltimo y as\u00ed sucesivamente.<\/li>\n<li style=\"text-align: justify;\"><code>LOOP_PINGPONG<\/code>: Los cuadros se dibujan del primero al \u00faltimo. Una vez llegado al \u00faltimo cuadro, se comienza a dibujar hac\u00eda atr\u00e1s, del \u00faltimo al primero. Al llegar otra vez al primero, se vuelve a dibujar hac\u00eda delante, y as\u00ed sucesivamente.<\/li>\n<li style=\"text-align: justify;\"><code>LOOP_RANDOM<\/code>: Los cuadros se dibujan aleatoriamente, sin un orden definido.<\/li>\n<\/ul>\n<\/li>\n<li style=\"text-align: justify;\"><strong>Tiempo transcurrido desde el comienzo de la animaci\u00f3n.<\/strong> Cada vez que dibujamos en pantalla, indicaremos a nuestra Animation el tiempo que ha transcurrido desde que comenz\u00f3 la animaci\u00f3n. En funci\u00f3n del tiempo y el modo de animaci\u00f3n configurado, nos devolver\u00e1 el cuadro que debe ser dibujado.<\/li>\n<\/ul>\n<p>&nbsp;<br \/>\n&nbsp;<\/p>\n<h2>Ejemplo<\/h2>\n<p style=\"text-align: justify;\">Normalmente, en un videojuego, cada personaje tendr\u00e1 varias animaciones y se dibujar\u00e1 una animaci\u00f3n u otra dependiendo del estado del personaje (andando, corriendo, saltando, golpeando, etc.). Para hacer este ejemplo lo m\u00e1s claro posible, voy a olvidarme de los estados y s\u00f3lo mostrar\u00e9 un \u00fanico elemento con una sola animaci\u00f3n en pantalla.<\/p>\n<p>&nbsp;<\/p>\n<h3>Im\u00e1genes<\/h3>\n<p style=\"text-align: justify;\">Para este ejemplo utilizar\u00e9 un archivo (<a href=\"https:\/\/github.com\/angelnavarro\/Gdx-Animation-Example\/blob\/master\/android\/assets\/charset.png\" target=\"_blank\">charset.png<\/a>) que contiene todas las im\u00e1genes que componen la animaci\u00f3n:<\/p>\n<p style=\"text-align: justify;\"><a href=\"http:\/\/www.pixnbgames.com\/blog\/wp-content\/uploads\/2015\/08\/charset1.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-425\" src=\"http:\/\/www.pixnbgames.com\/blog\/wp-content\/uploads\/2015\/08\/charset1.png\" alt=\"charset\" width=\"552\" height=\"237\" srcset=\"https:\/\/www.pixnbgames.com\/blog\/wp-content\/uploads\/2015\/08\/charset1.png 552w, https:\/\/www.pixnbgames.com\/blog\/wp-content\/uploads\/2015\/08\/charset1-300x129.png 300w\" sizes=\"(max-width: 552px) 100vw, 552px\" \/><\/a>Adem\u00e1s crear\u00e9 el archivo <a href=\"https:\/\/github.com\/angelnavarro\/Gdx-Animation-Example\/blob\/master\/android\/assets\/charset.atlas\" target=\"_blank\">charset.atlas<\/a>, sabiendo que cada cuadro de la animaci\u00f3n ocupa 80 p\u00edxeles de ancho por 120 de alto. En el atlas, utilizar\u00e9 el mismo nombre para todos los cuadros, asign\u00e1ndoles un \u00edndice a cada uno de ellos. Quedar\u00e1 algo as\u00ed:<\/p>\n<pre lang=\"Json\" line=\"1\">charset.png\r\nformat: RGBA8888\r\nfilter: Linear,Linear\r\nrepeat: none\r\nrunning\r\n  rotate: false\r\n  xy: 0, 0\r\n  size: 80, 120\r\n  orig: 80, 120\r\n  offset: 0, 0\r\n  index: 0\r\nrunning\r\n  rotate: false\r\n  xy: 80, 0\r\n  size: 80, 120\r\n  orig: 80, 120\r\n  offset: 0, 0\r\n  index: 1\r\nrunning\r\n  rotate: false\r\n  xy: 160, 0\r\n  size: 80, 120\r\n  orig: 80, 120\r\n  offset: 0, 0\r\n  index: 2\r\n(contin\u00faa)\r\n<\/pre>\n<p style=\"text-align: justify;\">De esta forma, podemos obtener f\u00e1cilmente todos los cuadros que componen la animaci\u00f3n:<\/p>\n<pre lang=\"Java\">public class GdxAnimationExample extends ApplicationAdapter {\r\n    ...\r\n    \/\/ Atlas con todos los cuadros definidos en \"charset.atlas\"\r\n    private TextureAtlas charset;\r\n    ...\r\n    \r\n    @Override\r\n    public void create () {\r\n        ...\r\n        \/\/ Carga de todas las imagenes definidas en \"charset.atlas\"\r\n        charset = new TextureAtlas( Gdx.files.internal(\"charset.atlas\") );\r\n        \r\n        \/\/ Obtencion de las imagenes de la animacion \"running\"\r\n        Array<AtlasRegion> runningFrames = charset.findRegions(\"running\");\r\n        ...\r\n    }\r\n    ...\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h3>Construir la animaci\u00f3n<\/h3>\n<p style=\"text-align: justify;\">Una vez tenemos cargadas las im\u00e1genes que componen la animaci\u00f3n, podemos construirla. Para este ejemplo repetiremos la animaci\u00f3n de indefinidamente, as\u00ed que usamos el modo <code>PlayMode.LOOP<\/code>:<\/p>\n<pre lang=\"Java\">public class GdxAnimationExample extends ApplicationAdapter {\r\n    ...\r\n    \/\/ Tiempo que permanece visible cada cuadro de la animacion\r\n    private static float FRAME_DURATION = .05f;\r\n\r\n    \/\/ Animacion del personaje corriendo\r\n    private Animation runningAnimation;\r\n    ...\r\n    \r\n    @Override\r\n    public void create () {\r\n        ...\r\n        \/\/ Construcci\u00f3n de la animacion\r\n        runningAnimation = new Animation(FRAME_DURATION, runningFrames, PlayMode.LOOP);\r\n        ...\r\n    }\r\n    ...\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h3>Dibujar cada cuadro<\/h3>\n<p style=\"text-align: justify;\">A la hora de dibujar, el objeto <code>runningAnimation<\/code> nos devoler\u00e1 el cuadro que debe ser dibujado en funci\u00f3n del tiempo que haya transcurrido. Por ello, tenemos la variable local <code>elapsed_time<\/code> donde vamos acumulando el tiempo transcurrido desde que la animaci\u00f3n comenz\u00f3:<\/p>\n<pre lang=\"Java\">public class GdxAnimationExample extends ApplicationAdapter {\r\n    ...\r\n    \/\/ Cuadro que debe ser dibujado en cada momento\r\n    private TextureRegion currentFrame;\r\n\r\n    \/\/ Acumulador del tiempo que lleva representada una animacion\r\n    private float elapsed_time = 0f;\r\n\r\n    \/\/ Variables auxiliares para saber donde pintar la imagen para que quede centrada\r\n    private float origin_x, origin_y;\r\n    ...\r\n    \r\n    @Override\r\n    public void render () {\r\n        ...\r\n        \/\/ Se acumula el tiempo transcurrido\r\n        elapsed_time += Gdx.graphics.getDeltaTime();\r\n        \r\n        \/\/ Se obtiene el cuadro que debe ser dibujado\r\n        currentFrame = runningAnimation.getKeyFrame(elapsed_time);\r\n\r\n        \/\/ Dibujo en pantalla\r\n        batch.begin();\r\n        batch.draw(currentFrame, origin_x, origin_y);\r\n        batch.end();\r\n        ...\r\n    }\r\n    ...\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h3>Ejemplo completo<\/h3>\n<p>As\u00ed queda la clase cuando unimos todo lo anterior:<\/p>\n<pre lang=\"Java\" line=\"14\">\/**\r\n * Ejemplo de animacion frame-by-frame (cuadro por cuadro) con LibGDX:\r\n * \r\n * http:\/\/www.pixnbgames.com\/blog\/libgdx\/animaciones-cuadro-por-cuadro-en-libgdx\r\n * \r\n * @author angel\r\n *\/\r\npublic class GdxAnimationExample extends ApplicationAdapter {\r\n    \r\n    \/\/ Tiempo que permanece visible cada cuadro de la animacion\r\n    private static float FRAME_DURATION = .05f;\r\n    \r\n    \/\/ Para dibujar en pantalla\r\n    private SpriteBatch batch;\r\n    \r\n    \/\/ Atlas con todos los cuadros definidos en \"charset.atlas\"\r\n    private TextureAtlas charset;\r\n    \r\n    \/\/ Cuadro que debe ser dibujado en cada momento\r\n    private TextureRegion currentFrame;\r\n    \r\n    \/\/ Animacion del personaje corriendo\r\n    private Animation runningAnimation;\r\n    \r\n    \/\/ Acumulador del tiempo que lleva representada una animacion\r\n    private float elapsed_time = 0f;\r\n    \r\n    \/\/ Variables auxiliares para saber donde pintar la imagen para que quede centrada\r\n    private float origin_x, origin_y;\r\n    \r\n    \r\n    @Override\r\n        public void create () {\r\n        batch = new SpriteBatch();\r\n        \r\n        \/\/ Carga de los cuadros definidos en \"charset.atlas\"\r\n        charset = new TextureAtlas( Gdx.files.internal(\"charset.atlas\") );\r\n        \r\n        \/\/ Se obtienen todas las imagenes para la animacion del personaje corriendo. Para hacer esto\r\n        \/\/ lo mas facil posible, hemos definido todos los cuadros con el mismo nombre, \"running\", y\r\n        \/\/ hemos asignado un indice (propiedad \"index\") a cada cuadro (ver \"charset.atlas\")\r\n        Array<AtlasRegion> runningFrames = charset.findRegions(\"running\");\r\n        \r\n        \/\/ Construcci\u00f3n de la animacion\r\n        runningAnimation = new Animation(FRAME_DURATION, runningFrames, PlayMode.LOOP);\r\n        \r\n        \/\/ Calculo de la posicion en la que dibujar para que quede centrado en pantalla\r\n        TextureRegion firstTexture = runningFrames.first();\r\n        origin_x = (Gdx.graphics.getWidth()  - firstTexture.getRegionWidth())  \/ 2;\r\n        origin_y = (Gdx.graphics.getHeight() - firstTexture.getRegionHeight()) \/ 2;\r\n    }\r\n    \r\n    \r\n    @Override\r\n    public void render () {\r\n        \/\/ Se limpia la pantalla aplicando un color naranja\r\n        Gdx.gl.glClearColor(1.0f, .8f, .667f, 1);\r\n        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);\r\n        \r\n        \/\/ Se acumula el tiempo que ha pasado desde la ultima vez que dibujamos. Con el tiempo total\r\n        \/\/ transcurrido es como el objeto Animation sabe el cuadro que toca dibujar ahora\r\n        elapsed_time += Gdx.graphics.getDeltaTime();\r\n        \r\n        \/\/ Obtencion del cuadro que debe ser dibujado\r\n        currentFrame = runningAnimation.getKeyFrame(elapsed_time);\r\n        \r\n        \/\/ Dibujo del cuadro\r\n        batch.begin();\r\n        batch.draw(currentFrame, origin_x, origin_y);\r\n        batch.end();\r\n    }\r\n    \r\n    \r\n    @Override\r\n    public void dispose() {\r\n        \/\/ Liberaci\u00f3n de recursos\r\n        charset.dispose();\r\n        super.dispose();\r\n    }\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h2>C\u00f3digo fuente<\/h2>\n<p>Puedes descargar el proyecto de ejemplo de <a href=\"https:\/\/github.com\/angelnavarro\/Gdx-Animation-Example\" target=\"_blank\">mi repositorio de GitHub<\/a>.<\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify;\">Saludos,<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introducci\u00f3n A la hora de animar los elementos\u00a0de nuestros videojuegos, uno de los\u00a0m\u00e9todos con los que contamos son las animaciones cuadro por cuadro (frame by frame).\u00a0Es el m\u00e9todo cl\u00e1sico de animaci\u00f3n, y consiste en dibujar una serie de im\u00e1genes de <a class=\"more-link\" href=\"https:\/\/www.pixnbgames.com\/blog\/libgdx\/animaciones-cuadro-por-cuadro-en-libgdx\/\">Seguir leyendo <span class=\"screen-reader-text\">  Animaciones cuadro por cuadro en LibGDX<\/span><span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":424,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[14],"tags":[42,10,15],"language":[5],"_links":{"self":[{"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/posts\/407"}],"collection":[{"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/comments?post=407"}],"version-history":[{"count":37,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/posts\/407\/revisions"}],"predecessor-version":[{"id":449,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/posts\/407\/revisions\/449"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/media\/424"}],"wp:attachment":[{"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/media?parent=407"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/categories?post=407"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/tags?post=407"},{"taxonomy":"language","embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/language?post=407"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}