pixelboyz logo
Desarrollo de Videojuegos

LibGDX Tutorial 10: Redes básicas

image

Índice de contenido


Vamos a analizar las redes para su aplicación LibGDX. Las redes en LibGDX son relativamente primitivas y solo admiten comunicaciones de socket. En muchos casos, sin embargo, eso es más que suficiente. Vamos a implementar una aplicación de chat basada en socket muy simple. El código está muy comentado, por lo que la discusión será bastante escasa. Si me perdí algo, por favor deje un comentario y haré todo lo posible para solucionarlo.

Muy bien, vamos a saltar directamente con el código:

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Net.Protocol;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.net.ServerSocket;
import com.badlogic.gdx.net.ServerSocketHints;
import com.badlogic.gdx.net.Socket;
import com.badlogic.gdx.net.SocketHints;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.TextArea;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

public class Networking implements ApplicationListener {
    private OrthographicCamera camera;
    private SpriteBatch batch;
    private Skin skin;
    private Stage stage;
    private Label labelDetails;
    private Label labelMessage;
    private TextButton button;
    private TextArea textIPAddress;
    private TextArea textMessage;
    
    // Pick a resolution that is 16:9 but not unreadibly small
    public final static float VIRTUAL_SCREEN_HEIGHT = 960;
    public final static float VIRTUAL_SCREEN_WIDTH = 540;
    
    
    @Override
    public void create() {        
        camera = new OrthographicCamera(Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
        batch = new SpriteBatch();
        
        // Load our UI skin from file.  Once again, I used the files included in the tests.
        // Make sure default.fnt, default.png, uiskin.[atlas/json/png] are all added to your assets
        skin = new Skin(Gdx.files.internal("data/uiskin.json"));
        stage = new Stage();
        // Wire the stage to receive input, as we are using Scene2d in this example
        Gdx.input.setInputProcessor(stage);

        
        // The following code loops through the available network interfaces
        // Keep in mind, there can be multiple interfaces per device, for example
        // one per NIC, one per active wireless and the loopback
        // In this case we only care about IPv4 address ( x.x.x.x format )
        List<String> addresses = new ArrayList<String>();
        try {
            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
            for(NetworkInterface ni : Collections.list(interfaces)){
                for(InetAddress address : Collections.list(ni.getInetAddresses()))
                {
                    if(address instanceof Inet4Address){
                        addresses.add(address.getHostAddress());
                    }
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }
        
        // Print the contents of our array to a string.  Yeah, should have used StringBuilder
        String ipAddress = new String("");
        for(String str:addresses)
        {
            ipAddress = ipAddress + str + "n";
        }
        
        // Now setupt our scene UI
        
        // Vertical group groups contents vertically.  I suppose that was probably pretty obvious
        VerticalGroup vg = new VerticalGroup().space(3).pad(5).fill();//.space(2).pad(5).fill();//.space(3).reverse().fill();
        // Set the bounds of the group to the entire virtual display
        vg.setBounds(0, 0, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
        
        // Create our controls
        labelDetails = new Label(ipAddress,skin);
        labelMessage = new Label("Hello world",skin);
        button = new TextButton("Send message",skin);
        textIPAddress = new TextArea("",skin);
        textMessage = new TextArea("",skin);

        // Add them to scene
        vg.addActor(labelDetails);
        vg.addActor(labelMessage);
        vg.addActor(textIPAddress);
        vg.addActor(textMessage);
        vg.addActor(button);
        
        // Add scene to stage
        stage.addActor(vg);
        
        // Setup a viewport to map screen to a 480x640 virtual resolution
        // As otherwise this is way too tiny on my 1080p android phone.
        stage.setViewport(VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT,false);
        stage.getCamera().position.set(VIRTUAL_SCREEN_WIDTH/2,VIRTUAL_SCREEN_HEIGHT/2,0);
        
        // Now we create a thread that will listen for incoming socket connections
        new Thread(new Runnable(){

            @Override
            public void run() {
                ServerSocketHints serverSocketHint = new ServerSocketHints();
                // 0 means no timeout.  Probably not the greatest idea in production!
                serverSocketHint.acceptTimeout = 0;
                
                // Create the socket server using TCP protocol and listening on 9021
                // Only one app can listen to a port at a time, keep in mind many ports are reserved
                // especially in the lower numbers ( like 21, 80, etc )
                ServerSocket serverSocket = Gdx.net.newServerSocket(Protocol.TCP, 9021, serverSocketHint);
                
                // Loop forever
                while(true){
                    // Create a socket
                    Socket socket = serverSocket.accept(null);
                    
                    // Read data from the socket into a BufferedReader
                    BufferedReader buffer = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
                    
                    try {
                        // Read to the next newline (n) and display that text on labelMessage
                        labelMessage.setText(buffer.readLine());    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start(); // And, start the thread running
        
        // Wire up a click listener to our button
        button.addListener(new ClickListener(){
            @Override 
            public void clicked(InputEvent event, float x, float y){
                
                // When the button is clicked, get the message text or create a default string value
                String textToSend = new String();
                if(textMessage.getText().length() == 0)
                    textToSend = "Doesn't say much but likes clicking buttonsn";
                else
                    textToSend = textMessage.getText() + ("n"); // Brute for a newline so readline gets a line
                
                SocketHints socketHints = new SocketHints();
                // Socket will time our in 4 seconds
                socketHints.connectTimeout = 4000;
                //create the socket and connect to the server entered in the text box ( x.x.x.x format ) on port 9021
                Socket socket = Gdx.net.newClientSocket(Protocol.TCP, textIPAddress.getText(), 9021, socketHints);
                try {
                    // write our entered message to the stream
                    socket.getOutputStream().write(textToSend.getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

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

    @Override
    public void render() {        
        Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        batch.setProjectionMatrix(camera.combined);
        batch.begin();
        stage.draw();
        batch.end();
    }

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

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

Y cuando ejecutes el código deberías ver:

Los dos valores superiores son las direcciones IP v4 de la máquina en la que se está ejecutando. Debe ingresarlos en la otra copia de la aplicación con la que desea chatear. Esta dirección identifica de manera única su máquina en Internet, mientras que especificamos el puerto (9021) usando un código. Piense en un puerto como un buzón en un edificio de apartamentos. La dirección de la calle del edificio es comparable a la dirección IP, mientras que PORT es similar al número de apartamento. Los valores de los puertos van desde 1 hasta 65.536, aunque se reservan varios. La mayoría de los puertos reservados son > 100, por lo que cuando elija un puerto para su aplicación, busque si esa dirección es un puerto dedicado (como 80 para comunicación HTTP o 21 para FTP), o simplemente elija un valor alto aleatorio. Su máquina puede tener múltiples direcciones, una por adaptador de red y posiblemente más, especialmente si ejecuta un software de virtualización como VMWare. Luego está el valor 127.0.0.1, esta es una dirección especial conocida como adaptador de bucle invertido y una dirección que apunta hacia sí misma.

Donde actualmente dice «Hello World», aquí es donde se mostrarán los mensajes entrantes.

En el primer cuadro de texto, ingrese la dirección IP de la máquina a la que desea enviar un mensaje. En este ejemplo, puede enviarse un mensaje a sí mismo, simplemente ingrese su propia dirección IP. Finalmente el segundo cuadro de texto es el texto del mensaje a enviar. Por supuesto, envía el mensaje presionando el botón Enviar mensaje.

Una cosa que puede notar en el ejemplo anterior es que lo configuré para que se ejecute en la resolución extraña de 540 × 960. ¿Por qué hice esto? Por un par de razones. En primer lugar, la resolución nativa del teléfono en el que probé (HTC One) es de 1920 × 1080 y la máscara/fuente predeterminada de Scene2D es demasiado pequeña para esa resolución. Por supuesto que podría haber creado una nueva piel que fuera más apropiada pero, bueno, soy flojo. La segunda razón es que la resolución es de un tamaño decente y tiene la misma relación de aspecto que 1080p (16:9), por lo que se escala bien, tanto hacia arriba como hacia abajo, cuando se muestra en una pantalla de 16:9. En un iPad se verá como basura absoluta. Quizás en el futuro haga una publicación específica sobre el manejo de múltiples resoluciones de dispositivos.

Hay una cosa más a tener en cuenta, este ejemplo actualmente no funcionará en iOS. El widget de Scene2D TextField actualmente no muestra el controlador de pantalla de iOS. Hay una corrección en el Foro de contribuciones de LibGDX. Es un truco/solución alternativa específico de iOS, por lo que no esperaría que se fusionara con el baúl principal. Finalmente, la red integrada de LibGDX es bastante simple, limitada solo a la programación de sockets. Para un soporte de red más sólido echa un vistazo a kryonet, es compatible con LibGDX y se informa que funciona en todas las plataformas LibGDX excepto HTML5. Lo cual no es realmente sorprendente, ya que el autor Nathan Sweet también es colaborador de LibGDX.

Parte anterior Tabla de contenido siguiente parte



Source link

Tags :
básicas,LibGDX,Redes,Tutorial

Comparte :

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *