Aquest article ve de jocs libGDX on implementem jocs multiplataforma.
libGDX es programa en Java, per tant podriem emprar les llibreries estàndard de comunicació HTTP o WebSockets. Però si utilitzem les pròpies de liBGDX ens facilitarà el codi, ja que ens estalviarem la gestió dels threads de comunicació necessaris a Android explicats a Android Threads.
Llibreries de comunicacions:
Les realitzem mitjançant la llibreria Networking de libGDX que ja ve integrada al propi framework.
Fes un projecte libGDX amb Dialog com el de l'exemple.
Implementa una crida HTTP a alguna web quan premem un botó del Dialog.
Podem seguir la doc oficial del plugin de WebSockets per a libGDX però amb un canvi, posant api
enlloc de compile
.
També caldrà la definició de la variable wsVersion a build.gradle
:
allprojects { ext { wsVersion = '1.9.10.3' }
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Si volem compilar versió HTML:
<inherits name='com.github.czyzby.websocket.GdxWebSocketGwt' /> <inherits name='com.github.czyzby.websocket.GdxWebSocket' />
I el més important, build.gradle
general del projecte (n'hi ha d'altres dins de cada plataforma):
allprojects { ext { wsVersion = '1.9.10.3' } repositories { maven { url "https://jitpack.io" } } } project(":desktop") { dependencies { implementation "com.github.MrStahlfelge.gdx-websockets:common:$wsVersion" } } project(":android") { dependencies { implementation "com.github.MrStahlfelge.gdx-websockets:common:$wsVersion" } } project(":html") { dependencies { implementation "com.github.MrStahlfelge.gdx-websockets:core:$wsVersion:sources" implementation "com.github.MrStahlfelge.gdx-websockets:html:$wsVersion" implementation "com.github.MrStahlfelge.gdx-websockets:html:$wsVersion:sources" } } project(":core") { dependencies { implementation "com.github.MrStahlfelge.gdx-websockets:core:$wsVersion" } }
També cal que inicialitzem els WebSockets als arxius AndroidLauncher
, iOSLauncher
i DesktopLauncher
, just abans de la creació del Launcher:
CommonWebSockets.initiate();
create()
.render
tot i que convé no abusar.WebSocketListener
.
El protocol WebSockets (ws://) funciona sobre HTTP. Si estem treballant en producció sobre HTTPS, el protocol és SecureWebSockets (wss://). El codi aquí indicat serà una mica diferent, caldrà instanciar WebSockets.toSecureWebSocketUrl
enlloc de WebSockets.toWebSocketUrl
.
La variable address
ha de ser el nom del servidor (no val la IP !), sense prefix, port 443 i emprant la crida que s'explica a continuació.
socket = WebSockets.newSocket(WebSockets.toSecureWebSocketUrl(address, port));
import com.github.czyzby.websocket.WebSocketListener; import com.github.czyzby.websocket.WebSocket; class GameScreen extends Screen { WebSocket socket; String address = "localhost"; int port = 8888; // constructor de l'objecte Screen public GameScreen { if( Gdx.app.getType()== Application.ApplicationType.Android ) // en Android el host és accessible per 10.0.2.2 address = "10.0.2.2"; socket = WebSockets.newSocket(WebSockets.toWebSocketUrl(address, port)); // ULL: si és a traves de HTTPS , el protocol seria wss enlloc de ws //socket = WebSockets.newSocket(WebSockets.toSecureWebSocketUrl(address, port)); socket.setSendGracefully(false); socket.addListener((WebSocketListener) new MyWSListener()); socket.connect(); socket.send("Enviar dades"); } // Es poden enviar dades al render() en tems real! // Millor no fer-ho a cada frame per no saturar el server // ni ralentitzar el joc public void render() { if( stateTime-lastSend > 1.0f ) { lastSend = stateTime; socket.send("Enviar dades"); } } // COMUNICACIONS (rebuda de missatges) ///////////////////////////////////////////// class MyWSListener implements WebSocketListener { @Override public boolean onOpen(WebSocket webSocket) { System.out.println("Opening..."); return false; } @Override public boolean onClose(WebSocket webSocket, int closeCode, String reason) { System.out.println("Closing..."); return false; } @Override public boolean onMessage(WebSocket webSocket, String packet) { System.out.println("Message:"); return false; } @Override public boolean onMessage(WebSocket webSocket, byte[] packet) { System.out.println("Message:"); return false; } @Override public boolean onError(WebSocket webSocket, Throwable error) { System.out.println("ERROR:"+error.toString()); return false; } } }
Podem emprar la llibreria WS per a NodeJS.
Ens caldrà crear un projecte NodeJS i instal·lar les llibreries:
$ mkdir ws1 $ cd ws1 $ npm init $ npm install http ws express uuid
Afegeix index.js
i posa'l en marxa:
$ node index.js
const { createServer } = require('http'); const { WebSocketServer } = require('ws'); const server = createServer(); const wss = new WebSocketServer({ server }); wss.on('connection', function connection(ws) { console.log("Nova connexió."); ws.send('something'); ws.on('error', console.error); ws.on('message', function message(data) { console.log('received: %s', data); }); ws.on('close', function close() { console.log("Tancant connexió.") }) }); server.listen( 8888, function() { console.log("listening..."); });
const express = require('express') const http = require('http'); const WebSocket = require('ws') const app = express() const httpServer = http.createServer(app); const wss = new WebSocket.Server({ server: httpServer }) const { v4: uuidv4 } = require('uuid') const port = 8888 var clients = {} // HTTP ROUTES app.use(express.static('public')) app.get('/',root); // WS client connections wss.on('connection', function connection(ws) { var userid = uuidv4(); console.log('Nova connexió: '+userid); clients[userid] = { "id":userid, "ws":ws, pos:{} }; ws.send("Benvingut id="+userid); // TODO: crear totems i actualitzar ws.on('close', function close() { delete clients[userid]; // TODO: esborrar totems? }) ws.on('error', console.error); ws.on('message', function message(data) { try { // exemple per descoficar JSON var posData = JSON.parse(data); posData.id = userid; console.log('Pos data: %s', JSON.stringify(posData)); // retransmetem posició a tothom broadcast(JSON.stringify(posData)); } catch (e) { console.log("ERROR descodificant pos: "+e) } }); }); // SERVER START httpServer.listen(port, appListen) function appListen () { console.log(`Example app listening on: http://localhost:${port}`) } // HTTP ///////////////////////////////// async function root(req,res) { res.send("IETI Game WebSocket Server"); } // WS : Web Sockets ///////////////////////////////// async function broadcast (obj) { for( var id in clients ) { var client = clients[id]; //if (client.readyState === WebSocket.OPEN) { var messageAsString = JSON.stringify(obj) client.ws.send(obj); //} } }
Implementa el servidor NodeJS indicat.
Afegeix la llibreria de WebSockets al teu joc libGDX i fes que envii la posició del nostre personatge 1 cop per segon.
Assegura't que funciona comprovant que el servidor mostra el missatge de posicionament del personatge a la seva consola.
Per treballar amb comunicacions és probable que us calgui codificar i descodificar JSON. Una opció és la llibreria Jackson de Java, que podem afegir al projecte amb Gradle:
project(":core") { dependencies { implementation 'com.fasterxml.jackson.core:jackson-core:2.10.1' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.10.1' implementation 'com.fasterxml.jackson.core:jackson-databind:2.10.1' } }