jueves, 16 de marzo de 2017

SpaceInvaderClon - Paso 2

SpaceInvaderClon - Paso 2

Hola buenas, 
Seguiremos en nuestro afán xD
En esta ocasión, haremos que nuestro nave espacial, se mueva, y dispare xD
Tanto para nuestro Escritorio(por medio de las flechas cursoras) y Android(por medio del acelerómetro).
Tambien cambiare un poco la metodología, porque creo que se entiende más ver el código fuentes, que tratar de explicar en texto xD
Si tienen alguna duda, pueden dejarla en los comentarios, inclusive puedo hacer otro post mas explicativo de los puntos que no entiendan.
Mano a la obra. Comencemos con la clase Asset, por el momento solo tendrá los nombre de nuestros sprites:
package sv.com.chuckle.game.spaceinvaderclon.utils;

public class Asset {
    public static String nombSpriteHero = "Starfighter.png";
    public static String nombSpriteMisil = "missile.png";
    public static String nombSpriteAlien = "alien.png";

}

En este momento, vamos a utilizar un patrón de diseño que LibGdx no proporciona. Es sencillo y muy útil. Es crear un Escenario y este contiene Actores, los cuales actúan es nuestro Escenario, esto es representado:
Stage stage = new Stage();
Actor hero = new Actor();
stage.addActor(hero);
Ya en nuestro render, llamar:
stage.act();
stage.draw();
Claro, falta la parte donde el hero, debe de tener su actuacion, el metodo act() llama a todos los act de todos los actores.
Entonces creamos la clase GameObject que extiende de la clase Image(esta clase extiende de Actor), y va hacer nuestro clase común, donde se crearán nuestros Sprites, aquí lo importante está la clase Rectangle que es una clase geométrica, la cual no es pintada en la pantalla, pero si funciona a nivel lógico, y la cual será utilizada para ver si está sobrepuesta sobre otro rectángulo, osea las COLISIONES. Este rectángulo tiene las dimensiones de la imagen del sprite y se moverá en las mismas coordenadas que el sprite.
package sv.com.chuckle.game.spaceinvaderclon.utils;

import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.ui.Image;

public class GameObject extends Image {
    private Rectangle rectangle;

    public GameObject(String nombTexture,float x,float y){
        super(new Texture(nombTexture));
        rectangle = new Rectangle(x,y,getWidth(),getHeight());
        setPosition(x,y);
    }

    @Override    public void act(float delta) {
        super.act(delta);
        rectangle.setPosition(getX(),getY());
    }

    public Rectangle getRectangle() {
        return rectangle;
    }
}
Crearemos la clase misil:
package sv.com.chuckle.game.spaceinvaderclon.entidad;

import sv.com.chuckle.game.spaceinvaderclon.utils.Asset;
import sv.com.chuckle.game.spaceinvaderclon.utils.GameObject;
import sv.com.chuckle.game.spaceinvaderclon.utils.Utils;

public class Misil extends GameObject{
    private float vel;

    public Misil(float x, float y){
        super(Asset.nombSpriteMisil,x,y);
        vel = 300;
    }

    @Override    public void act(float delta) {
        super.act(delta);
        moveBy(0,vel*delta);

        if(getY() > Utils.ALTO)
            remove();

    }
}
Observaciones: una variable flotante vel, esta variable representa la velocidad del misil. En el método ¨act¨, vemos el método moveBy, esta acción suma un desplazamiento en x e y, si observamos en x no se moverá, solo en y; esto moverá de forma vertical el sprite, por un corto desplazamiento, pero el método act esta en nuestro render, así que se está ejecutando cada momento, así que da la impresión que es un movimiento fluido. Luego observamos que se pregunta si la posición de Y es mayor que nuestro ALTO, si es así, removemos nuestro actor de la escena, esto hace que se libere memoria.

Creamos la clase Alien, pero por el momento solo aparecera pintada en nuestra pantalla.

Continuas creando la clase Hero, aqui esta la carnita de este tutorial:
package sv.com.chuckle.game.spaceinvaderclon.entidad;

import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.utils.TimeUtils;

import sv.com.chuckle.game.spaceinvaderclon.utils.Asset;
import sv.com.chuckle.game.spaceinvaderclon.utils.GameObject;
import sv.com.chuckle.game.spaceinvaderclon.utils.Utils;


public class Hero extends GameObject{
    private float x;
    private float vel = 250f;
    private ESTADO estado;
    private long lastFire;

    enum ESTADO{
        nada        ,derecha        ,izquierda    }

    public Hero(float x, float y) {
        super(Asset.nombSpriteHero, x, y);
    }

    @Override    public void act(float delta) {
        super.act(delta);

        if(Gdx.input.isTouched()){
            if(TimeUtils.millis() - lastFire >= 300){
                Misil misil = new Misil(getX()+getWidth()/2-10, getY()+getHeight());
                getStage().addActor(misil);
                lastFire = TimeUtils.millis();
            }
        }

        Application.ApplicationType appType = Gdx.app.getType();
        switch (appType){
            case Desktop:
                if(Gdx.input.isKeyPressed(Input.Keys.LEFT))
                    estado = ESTADO.izquierda;
                else if(Gdx.input.isKeyPressed(Keys.RIGHT))
                    estado = ESTADO.derecha;
                else                    estado = ESTADO.nada;
                break;
            case Android:
                if(Gdx.input.getAccelerometerX() < 0)
                    estado = ESTADO.derecha;
                else if(Gdx.input.getAccelerometerX() > 0)
                    estado = ESTADO.izquierda;
                else                    estado = ESTADO.nada;
                break;
        }//fin switch
        x = getX();
        if(estado == ESTADO.derecha)
            x += delta*vel;
        if(estado == ESTADO.izquierda)
            x -= delta*vel;

        if(x<=0)
            x = 0;
        else if(x>= Utils.ANCHO-getWidth())
            x = Utils.ANCHO-getWidth();

        setPosition(x, 0);
    }
}
Observaciones: Esta parte se las dejo a ustedes que descifren que quiero decir xD.
Quiza lo unico que les puedo ayudar, es TimeUtils.millis(), con la cual obtenemos el tiempo transcurrido en milisegundos, entonces aquí estamos dando 300 milisegundos para que aparezca otro disparo.

Luego, reescribimos la clase GameScreen, la cual se debe de implementar de Screen. Ver código fuente.

Por último reescribimos la clase GameLoop:
package sv.com.chuckle.game.spaceinvaderclon;

import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;

import sv.com.chuckle.game.spaceinvaderclon.screen.MainScreen;
import sv.com.chuckle.game.spaceinvaderclon.utils.Asset;

public class GameLoop extends Game {

   public GameLoop(){

   }

   @Override   public void create() {
      setScreen(new MainScreen(this));
   }

   @Override   public void render() {
      getScreen().render(Gdx.graphics.getDeltaTime());
   }
}
Observaciones: Aqui lo importanet es el setScreen xD
Resultado:


Codigo fuente aqui

Si te gusta mi trabajo y crees que vale la pena que sigamos aprendiendo, pues dona un dólar
para esta buena causa xD

lunes, 27 de febrero de 2017

SpaceInvaderClon - Paso 1

SpaceInvaderClon - Paso 1

Hola buenas, 
Seguimos en nuestro afán, de nuestro primer juego. Aquí puedes ver la parte previa PASO 0
En este paso 1, vamos a crear la lógica entre pantalla, así seguimos avanzando.
Primero creamos la clase Utils y creamos el paquete: sv.com.chuckle.game.spaceinvaderclon.utils
Código clase Utils:
package sv.com.chuckle.game.spaceinvaderclon.utils;

import com.badlogic.gdx.Gdx;

public class Utils {
    public static int ANCHO = 480;
    public static int ALTO = 650;
    public static boolean IS_DEBUG = true;

    public static void log(String tag,String message){
        if(IS_DEBUG)
            Gdx.app.log(tag, message);
    }
}

En esta clase, hemos definido de forma estática, el ancho y alto de nuestra pantalla.
Los valores 480x650, hacen referencia una pantalla en vertical(Portrait). También hemos 
definido una variable tipo booleana llamada "IS_DEBUG", la cual será nuestra bandera 
si deseamos escribir en la consola y para dibujar los elementos geométricos, para las 
colisiones.


Modificar pantalla Desktop y Android
Para ello primero nos dirigiremos a la clase DesktopLauncher, de nuestro subproyecto 
"desktop", aqui agregamos las definiciones del ANCHO y ALTO, de la clase Utils, asi:

package sv.com.chuckle.game.spaceinvaderclon.desktop;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import sv.com.chuckle.game.spaceinvaderclon.GameLoop;
import sv.com.chuckle.game.spaceinvaderclon.utils.Utils;

public class DesktopLauncher {
   public static void main (String[] arg) {
      LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
      config.width = Utils.ANCHO;
      config.height = Utils.ALTO;
      config.resizable =  false;
      config.title = "Space Invader - CLON";
      new LwjglApplication(new GameLoop(), config);
   }
}
Creo que se entiende que se está haciendo.

Luego vamos por el archivo de configuración de android el AndroidManifest.xml 
del subproyecto "android' y cambiaremos la linea donde se encuentre la etiqueta:
android:screenOrientation, por esto: android:screenOrientation="portrait"


Crear las Pantallas
Para esto creamos el paquete: 
sv.com.chuckle.game.spaceinvaderclon.screen
Y creamos tres clases llamadas: MainScreen, GameScreen y GameOverScreen.
Estas clases deben de implementarse con la interfaz llamada: Screen
Y su constructor debe de recibir como parámetro el objeto GameLoop, que es el que extiéndete de Game(Este controla nuestro juego).
Clase MainScreen:
package sv.com.chuckle.game.spaceinvaderclon.screen;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

import sv.com.chuckle.game.spaceinvaderclon.GameLoop;
import sv.com.chuckle.game.spaceinvaderclon.utils.Utils;

public class MainScreen implements Screen{
    private GameLoop game;
    private BitmapFont font;
    private Batch batch;

    public MainScreen(GameLoop game){
        this.game = game;
        font = new BitmapFont();
        batch = new SpriteBatch();
    }

    @Override    public void show() {

    }

    @Override    public void render(float delta) {
        Gdx.gl.glClearColor(0, 0, 0, 0);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        if(Gdx.input.justTouched())
            game.setScreen(new GameScreen(game));
        batch.begin();
        font.draw(batch, "SPACE INVADER", Utils.ANCHO/2-60, Utils.ALTO/2+100);
        font.draw(batch, "Presione sobre la pantalla para iniciar el juego", Utils.ANCHO/2-150, Utils.ALTO/2+50);
        batch.end();

    }

    @Override    public void resize(int width, int height) {

    }

    @Override    public void pause() {

    }

    @Override    public void resume() {

    }

    @Override    public void hide() {

    }

    @Override    public void dispose() {
        font.dispose();
        batch.dispose();
    }
}

Las otras dos clases son iguales, solo cambian las posiciones del texto.
Clase GameScreen:
@Overridepublic void render(float delta) {
    Gdx.gl.glClearColor(0, 0, 0, 0);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    if(Gdx.input.justTouched())
        game.setScreen(new GameOverScreeen(game));
    batch.begin();
    font.draw(batch, "Pantalla De Juego", Utils.ANCHO/2-50, Utils.ALTO/2+100);
    font.draw(batch, "Presione sobre la pantalla para continuar", Utils.ANCHO/2-120, Utils.ALTO/2+50);
    batch.end();

}

Clase GameOverScreen:
@Overridepublic void render(float delta) {
    Gdx.gl.glClearColor(0, 0, 0, 0);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    if(Gdx.input.justTouched())
        game.setScreen(new GameScreen(game));
    batch.begin();
    font.draw(batch, "GAME OVER", Utils.ANCHO/2-50, Utils.ALTO/2+100);
    font.draw(batch, "Presione sobre la pantalla para iniciar el juego", Utils.ANCHO/2-150, Utils.ALTO/2+50);
    batch.end();

}
Para fines prácticos, el flujo por el momento es:
MainScreen->GameScreen<->GameOverScreen

Paso Final:
Para nuestro final, solo falta modificar la clase GameLoop, para que sea esta la que controle todo nuestro juego. Para ello limpiamos toda la clase y la extendemos de Game, asi:
package sv.com.chuckle.game.spaceinvaderclon;

import com.badlogic.gdx.Game;

import sv.com.chuckle.game.spaceinvaderclon.screen.MainScreen;

public class GameLoop extends Game {

   @Override   public void create() {
      setScreen(new MainScreen(this));
   }
}
Como Game, tiene la capacidad de poner que pantalla esté activa, de esta forma al iniciar
nuestro juego, la pantalla por defecto será la MainScreen.
Ya podemos ejecutar nuestro subproyectos, y veran como nos quedó.
Aquí el resultado:


Como vemos es super sencillo hacer este juego de pantallas.
Para nuestra próxima entrega,  ya pintaremos nuestra nava y haremos que se mueva y dispare xD

Codigo fuente aqui.

Saludos

Si te gusta mi trabajo y crees que vale la pena que sigamos aprendiendo, pues dona un dólar
para esta buena causa xD

domingo, 26 de febrero de 2017

SpaceInvaderClon - Paso 0

Hola Buenas.

Ahora comenzamos con nuestro primer juego pedagógico.

Claraciones:

  • He tenido varios problemas con la versión más reciente del LibGDX(1.9.5). Sospecho que es por que hace más de 4 años que tengo instalado el SDK de android. He hecho varias pruebas si casi siempre me falla cuando quiero trabajar con la librería Box2D. Pero he logrado hacer funcionar todo con la última versión de LigGDX 1.9.5 y Android Studio 2.23, así que para este proyecto utilizaremos estas versiones.
  • Los artes las buscaremos en http://opengameart.org/

Manos a la Obra:

Como ya hemos dicho antes, descargamos la versión 1.9.5 de LibGDX aquí, en teoría con esta versiones podemos modificar que version de LibGDX queremos utilizar, guia aqui.
Por cualquier cosa también podemos descargar versiones más viejas aquí.
Una vez tengamos descargado, descomprimimos estos archivos en algun de fácil acceso, de preferencia en la raíz o home, por ejemplo C:/(si estamos en Windows).

Siguiente paso, descargar los assets, para hecho nos vamos a la pagina http://opengameart.org/,
para efectos prácticos ya los tengo listo, puedes cargarlo de aquí
Recomiendo encarecidamente tener actualizado su android studio.

Nuestro juego será más o menos como el mítico Space Invader, tratara de nuestro nave estara en la base de la pantalla, y solo se movera de izquierda y derecha, y presionando sobre la pantalla disparara hacia arriba. Por otro lado los enemigos bajarán de forma constante y aparecerán de forma aleatoria en el eje X.

Creamos una carpeta donde se generarán los archivos de LibGDX, yo utilizare: C:\workspace_libgdxelsalvador\spaceInvaderClon, le damos doble click gdx-setup.jar, de los archivos recién descargados. Y colocamos los siguientes valores:
Y presionamos el Botón Generate, y damos click en "SI" o "YES" de las advertencias que nos aparezcan. Esperamos que finalice, luego ya estamos listo para importar en nuestro Android Studio:
Luego importamos nuestro proyecto, elegimos la opción: Import Project y elegimos la carpeta donde esta los archivos generados(en mi caso: C:\workspace_libgdxelsalvador\spaceInvaderClon)

Android Studio nos dará una recomendación, en este caso le diremos que si Actualice(Update):

Esperemos que finalice toodo el proceso de actualización de Gradles, una vez se finalice se debe de configurar que el Android Studio ejecute nuestro sub proyecto de Desktop, como aplicación.
Para ello elegimos el sub proyecto "desktop" y damos click sobre Edit Configuratios...

Damos click al botón "+" y elegimos la opción "Application":

Aquí hay que llenar los datos que se nos piden, podemos hacer uso de los botones que nos ayudan a buscar por medio de un explorador. Aquí podemos resaltar la opción Working directory, es donde se encuentran nuestros ASSETS:
Aplicamos los cambio y presionamos el OK.
Listo, ya podemos ejecutar nuestro subproyecto Desktop.

Haremos algo de limpieza antes de comenzar todo.
En nuestro subproyecto "android", eliminamos la imagen "badlogic.jpg" de la carpeta "assets" y luego copias todos los archivos descargados de nuestro assets_libgdxelsalvador.blogspot.com.zip, siempre en la carpeta "assets", así:

En la clase "GameLoop", del subproyecto "core", lo dejamos así:
package sv.com.chuckle.game.spaceinvaderclon;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class GameLoop extends ApplicationAdapter {
   SpriteBatch batch;
   Texture img,img2,img3;

   @Override   public void create () {
      batch = new SpriteBatch();
      img = new Texture("alien.png");
      img2 = new Texture("Starfighter.png");
      img3 = new Texture("missile.png");
   }

   @Override   public void render () {
      Gdx.gl.glClearColor(1, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
      batch.begin();
      batch.draw(img, 0, 0);
      batch.draw(img2, 100, 0);
      batch.draw(img3,200,0);
      batch.end();
   }
   
   @Override   public void dispose () {
      batch.dispose();
      img.dispose();
      img2.dispose();
      img3.dispose();
   }
}

Guardamos, y ejecutamos el subproyecto desktop:

 También podemos correr el proyecto de "android", para ello tenemos que tener un dispositivo físico conectado a nuestro computador o algún emulador.
Listo ya tenemos nuestro ambiente listo para comenzar con la logica xD
En la siguiente entrega, haremos el juego entre pantallas de los juegos.
Codigo fuente aqui
Saludos

Si te gusta mi trabajo y el esfuerzo, pues puedes donar un $1, a esta causa xD

sábado, 25 de febrero de 2017

2017 - Disculpas, Continuidad y Más

Hola Buenas.
Se que no he continuado con el contenido que me había propuesto. Por eso me disculpo sinceramente.
Prometo subir material por lo menos una vez al mes en el peor de los casos.

Continuaré donde nos habíamos quedado, en seguir preparándonos para hacer un juego funcional, que ponga en práctica todo lo que hemos aprendido. La meta es hacer un mini juego que cumpla con todo el ciclo de vida de un videojuego normal, claro este juego es con fines pedagógicos.
Que incluirá este juego:

  1. Manejo de pantallas(Principal, Juego, GameOver, etc.).
  2. Manipulación de sonidos y música de fondo.
  3. Colisiones entre los diferentes objetos.
  4. Manipulación de controles para nuestro juego(Desktop: por medio de las flechas cursoras y Android: por medio del giroscopio.)
  5. Claro, manipulación de los Sprite. 
Y que viene luego de nuestro gran proyecto?
Pues existen muchas cosas un poco más avanzadas, que iremos aprendiendo de poco a poco.
Entre la cuales podemos mencionar:
  1. Editor de Mapas por medio de patrones: Tiled
  2. Cómo utilizar la librería Box2D, puedes ver algunos demos aquí.
  3. Editor de niveles como Overlap2D y VisEditor.
  4. Editor de Sistemas de Partículas.
  5. Técnicas de rendimiento(Sprite Sheet, Asset Manager y Pooling).
  6. Manipulación de GUI.
  7. Manipulación de FONT.
  8. ETC.
Como ven esto se pone SABROSO, así que pendientes xD

En diciembre del año pasado(2016), fue liberada la versión 1.9.5 de libgdx. He tratado de trabajar con él y resulta que tiene unos problemas con la sincronización de las API's de Android. Quizá sea porque tengo ratos de no actualizar mis SDK de Android, aún utilizado Eclipse.
Por esta razón, utilizaremos la versión 1.6.1 LibGdx, la cual podemos descargar de AQUI

Desde este punto esta es la versión que utilizaremos en todos los artículos del presente Blog.

Como siempre todo el contenido de este Blog, estará en GitHub y tratare de colgar un video demostrativo.

Creo que es todo xD
El siguiente post, estaremos preparando todo el ambiente, sonidos, música y sprite. Para comenzar nuestro proyecto del juego. Lo que no se, cuantas partes o tutoriales haré para terminar el juego.

Nos vemos la otras semana, ya con el inicio del juego insignia xD

Saludos.