Technisch Rapport: Tap to Race
Samuel Moyson Van de Walle Tibbout
Begeleidende docent: Alexander De Smet
Inhoud Inleiding ...................................................................................................................................................... 3 Game files ................................................................................................................................................... 3 Cliënt-side.js ......................................................................................................................................... 3 index.html ........................................................................................................................................ 3 index.js ............................................................................................................................................... 3 start.js................................................................................................................................................. 3 game.js ............................................................................................................................................... 4 tap.js .................................................................................................................................................... 8 stop.js ................................................................................................................................................. 8 Server-side.js ....................................................................................................................................... 8 server.js ............................................................................................................................................. 8 Config files ................................................................................................................................................. 9 Procfile............................................................................................................................................... 9 package.json .................................................................................................................................... 9 Heroku ......................................................................................................................................................... 9 Libraries ................................................................................................................................................... 10 node.js ............................................................................................................................................. 10 pixi.dev.js ....................................................................................................................................... 10 socket.io-1.0.4.js ......................................................................................................................... 10 express.js ....................................................................................................................................... 10 howler.js ........................................................................................................................................ 10 Assets ........................................................................................................................................................ 10 Images ............................................................................................................................................. 10 Sound............................................................................................................................................... 10 Besluit ....................................................................................................................................................... 11 Referenties.............................................................................................................................................. 12
2
Inleiding Tap To Race is een multiplayer game waarbij een gebruiker via smartphone surft naar een heroku website, waar de game gehost wordt. Daarna wordt hij in een wachtroom gezet tot er een andere speler connecteert. Van zodra er twee spelers geconnecteerd zijn, kunnen zij spelen tegen elkaar. Doel is om onder het snelst een race parcours af te leggen en daarbij objecten te ontwijken. Sneller racen gebeurt met te tappen op een button, alsook objecten ontwijken. Voor deze game hebben wij gebruikt gemaakt van HTML5 en enkel Javascript libraries. Node.js en Socket.io hebben we gebruikt om de multiplayer functionaliteit op te zetten. Pixi.js, Howler.js en Express.js hebben we gebruikt om de game functionaliteiten te voorzien.
Game files Cliënt-side.js index.html
De volledige bouw van de site wordt hierin opgezet. Tussen de tags staan de <meta> tags die de site als webapp weergeven op mobiele Apple toestellen en toestellen met het besturingssyteem Androïd. Daarnaast zit er ook een klein stukje css in om de basis layout in te stellen. Tussen de tags worden alle javascript bestanden opgeroepen die het eigenlijke spel aanmaken.
index.js In dit bestand wordt het spel gestart. start.js Dit script wordt gebruikt om het startscherm weer te geven aan de speler. Eerst worden de basis Pixi.js instellingen ingesteld zodat het beeld correct geschaald wordt voor de speler en daarna wordt de eigenlijke weergave opgebouwd. Dat gebeurt met een build function die de verschillende componenten van de weergave oproept.
3
In dit geval bestaat de build function uit een resize function die de responsive weergave verzorgt, een createTextures function die de afbeeldingen inlaadt, een drawScene function die de scene zelf tekent met Pixi.js, een drawClouds function die de wolken weergeeft en tot slot een drawLocal function die de buttons weergeeft. Daarnaast worden er ook nog twee Render functions opgeroepen, die het beeld renderen. build: function() { // Responsive design this.resize(); // Create textures this.createTextures(); // Draw example scene this.drawScene(); // Draw clouds this.drawClouds(); // Draw start page this.drawLocal(); // Render the background once. this.bgRenderer.render(this.bgStage); // Render the stage for the current frame. this.renderer.render(this.stage); },
game.js
In Game.js wordt de game zelf client-side opgebouwd. In de var Game creëren we de scène in Pixi.js waar we later dan objecten zullen op toevoegen. Daarnaast maken we ook game variabelen aan in deze methode. Naast deze game variabelen, verzorgen we hier ook de communicatie met onze Node.js server. De communictie met de server gebeurt door de functies besproken onder server-side.js te binden aan de socket instantie die aangemaakt is in de Game variabele. // Voorbeeld van het binden van een serverside functie. this.socket.on(‘functionName’, function (parameters)); … }.bind(this));
Het sturen van data naar de server gebeurt doormiddel van ‘socket.emit’ met de naam van de functie die aangesproken dient te worden en er de te zenden data als array aan mee te geven. // Voorbeeld van het zenden van data naar de server. this.socket.emit(‘functionName’, { param1: this.local1, param2: this.local2 });
4
Net zoals bij Start.js bouwen de Pixi.js scene ook hier op met een build function waarin we dan de verschillende componenten oproepen met verschillende methoden. We kunnen deze opdelen in methoden die zorgen voor het grafische gedeelte van de game, methoden die de logica verzorgen van obstakels die verschijnen en wat te doen bij een botsing van obstakel met speler en tot slot methoden om de randelementen in de game te definiëren zoals score, tijd, geluid. In de methode createTextures laden we alle afbeeldingen in die we nodig hebben voor de game. Deze wijzen we toe aan een Texture in Pixi. Voor de loadbar op het wachtsterm maken we gebruik van een array om de verschillende frames in op te slaan. for(var i=0; i <= 9; i++) { this.loadbarTexture = PIXI.Texture.fromImage("./images/loadbarfixed/IMG0000" + i + ".gif"); this.loadbarTextures.push(this.loadbarTexture); } for(var i=10; i <= 28; i++) { this.loadbarTexture = PIXI.Texture.fromImage("./images/loadbarfixed/IMG000" + i + ".gif"); this.loadbarTextures.push(this.loadbarTexture); }
De setupBoundaries methode bakent de grenzen van de scene af. In de setupAudio methode laden we het geluid in voor het spel met behulp van de Howler.js javascript library. Geluid inladen doen we via een Howl object waar we een pad aan meegeven en een volume voor het geluid. this.cowsound = new Howl({ urls: ['./sounds/cowmoo.ogg', './sounds/cowmoo.mp3'], volume: 0.9 });
De drawWaiting methode creëert het wachtscherm wanneer spelers moeten wachten tot er een tweede speler geconnecteerd is om een game te starten. Er wordt een background in Pixi aangemaakt, waarop een tekst geplaatst wordt. Onder die tekst wordt dan met behulp van een Movieclip in Pixi een loadbar gecreëerd bestaande uit de reeds eerder ingeladen frames in createTextures. drawTrees tekent de bomen op het scherm aan weerszijden van de racebaan. Bij de plaatsing wordt rekening gehouden met de positie van de boom ten opzichte van de speler en zodus wijzigt de schaling dus ook met de positie.
5
moveTrees zorgt er dan voor dat de bomen bewegen in het spel wat de speler het optische gevoel geeft dat de weg beweegt. De snelheid waarmee de bomen bewegen wordt bepaald door de snelheid waarmee de speler tapt. Wanneer de speler tapt wordt de snelheid verhoogt, wanneer hij stopt met tappen vertraagt de snelheid. Als de speler botst met een obstakel, dan wordt zijn snelheidswaarde gereset. In deze methode wordt het aantal taps ook gecommuniceerd naar de server onder de vorm van een score waarde, zodat de tegenstander het aantal taps van de speler ook kan zien op zijn scherm. // Broadcast the score update to all clients. this.socket.emit('score', { obstacle: this._score[this._player], score: Math.round(this.treeSpeedX*42), treeSpeedX: this.treeSpeedX, treeSpeedY: this.treeSpeedY, treeScale: this.treeScale, plr: this._player });
drawLocal bouwt de basiscène met racebaan en horizon op met Pixi elementen. drawObstacle laat een object verschijnen op de baan afhankelijk van een aantal taps dat random genereerd wordt door de server. Voor beide clients is dit aantal hetzelfde, zodat de uitdaging voor beide spelers dezelfde blijft. Er zijn twee soorten objecten die kunnen verschijnen, aan de hand van Math.random wordt bepaald welk object verschijnt. De methode moveObstacle zorgt er, net zoals moveTrees bij de bomen, voor dat de obstakels kunnen bewegen over de baan. Afhankelijk van de posities op de baan van de obstakels, links of rechts, worden de x en y waarden aangepast. In tegenstelling tot de bomen, zijn het hier constante waarden, niet afhankelijk van de taps. detectCollision detecteert of een speler een obstakel raakt. Aangezien de auto van de speler niet verplaatst wordt in de y richting, dienen we een check te doen op de x posities van obstakel en speler om een botsing te detecteren. Wanneer een botsing plaatsvindt, verdwijnt het obstakel uit beeld, wordt een geluid afgespeeld en wordt de score van de speler met een penalty verminderd waarna de snelheid ook gerest wordt. if(this.carSprite.position.x < 300 & this.obstacleSprite.position.x <= 310) { if (this.kind == 'cow') { this.cowsound.play(); } else if (this.kind == 'shit') { this.shitsound.play(); } console.log("botsing:"); this._score[this._player] -= 10; this.obstacleSprite.position.y = 10000; this.treeSpeedX = 0.135; this.treeSpeedY = 0.1; this.treeScale = 0.00009; }
6
De progressbar zorgt ervoor dat een speler kan zien hoever hij zelf zit op de racebaan en hoever de tegenstander zit. Zijn eigen positie wordt in groen weergegeven, die van de tegenstander in rood. Deze bar wordt opgebouwd met twee methoden, de drawProgressBar methode en de moveProgressBar. Deze methoden functioneren analoog als bij de bomen en obstakels. De positie van de tegenstander wordt verworven via een door de server doorgestuurde waarde. createCar definieert de auto van de speler. De wolken worden toegevoegd aan de scene met behulp van de methoden drawClouds en moveClouds. Ook deze methoden werken naar analogie met de andere draw en move methoden. Tot slot hebben we nog de strepen op de weg die gecreëerd worden met de drawRoadStripes en moveRoadStripes methoden. Zo wordt de gehele scène van een speler opgebouwd. We hebben ook nog methoden gevolgd door Rival die de scène van de tegenstander onderaan opbouwen en aanpassen met door de server doorgestuurde variabalen. De snelheid van de tegenstander wordt steeds geüpdatet. Naast al deze methoden om de scènes op te bouwen, zijn er ook nog methoden om game functionaliteiten te voorzien. De setupScore methode verzorgt de tapupdates van de spelers en geeft deze weer op de desbetreffende scène. setupTimer houdt de racetijd bij. switchCarPosition zorgt ervoor dat de auto van de speler links of rechts op de baan rijdt. Dit wordt bepaald door te tappen op de linkse tapbutton. De positie van de speler wordt doorgestuurd naar de server zodat de tegenstander ook kan zien op welk rijvak de speler rijdt. defineTapArea creëert twee tapbuttons waar de speler de game mee kan aansturen. De linkse button is om van rijvak te wisselen om objecten te ontwijken, de rechtse is sneller te rijden. Beide buttons zijn Pixi elementen. updatePhysics roept de eerdere move methoden open zorgt ervoor dat deze effectief uitgevoerd worden. Daarnaast checkt deze methode ook welke speler wint en beëindigt het spel van zodra een speler wint. if(this._score[this._player] == 100) { this.socket.emit('endGame'); this.gameStatus = "You Win"; this.music.unload(); var stop = new Stop(this._score[this._player], this.timeUpdate/1000, this.gameStatus); }
Ook heft deze methode het wachtscherm op wanneer een tweede speler connecteert met behulp van de found variabele die doorgestuurd wordt door de server. Tot slot hebben we nog de tick methode. Deze methode is een hele belangrijke, ze zorgt er namelijk voor dat het spel beweegt. Deze methode zal 60 frames per seconde genereren van de scène. Daarnaast zorgt ze er ook voor dat de auto trilt, waardoor de game ervaring beter wordt.
7
tap.js De acties gekoppeld aan het tappen op de tapvelden in de game variabele zijn bepaald in dit bestand. Een eerste aanroeping gebeurt wanneer het spel door de gebruiker op het startscherm wordt gestart, hiermee wordt de game variabele aangemaakt. Er staan 2 tapvelden beschreven namelijk: ‘tapSpriteRight’ => hiermee wordt de score en spelsnelheid verhoogt. ‘tapSpriteLeft’ => hiermee switch de speler van rijvak. Deze velden zijn tevens de besturing van het spel.
stop.js Dit javascript bestand wordt op dezelfde manier opgebouwd als start.js met dat verschil dat op dit scherm een restart button getoond word en een samenvatting van het spel met de mededeling of je gewonnen hebt of niet. Wanneer op de restart button geklikt wordt, dan wordt de speler terug geleid naar de startpagina van het spel. window.open("http://tap-to-race.herokuapp.com","_self");
De variabelen om de game statistieken zoals tijd en score weer te geven worden ontvangen van het game.js script.
Server-side.js server.js Eerst en vooral creëren we hier onze serverinstantie en laten we onze server luisteren op events van de clients. Daarnaast laden we ook enkele libraries in die gebruikt zullen worden door de node.js javascript engine. var express = require('express'); var app = express(), http = require('http'), server = http.createServer(app), io = require('socket.io').listen(server);
Eén van deze libraries is express.js. Deze librarie wordt gebruikt om de routes in onze game te verzorgen. Zo moet elke pad van een afbeelding geconfigureerd worden in deze routes, alsook de paden naar de javascript bestanden. Zo een route wordt in express als volgt gecreëerd. app.get('/sounds/racecar.wav', function(req, res){ res.sendFile(__dirname + '/sounds/racecar.wav'); });
Naast het inladen van alle assetbestanden wat onder Express besproken wordt, bevat dit bestand de serverside logica voor het multiplayer aspect van de game. Dit bestand wordt niet ingeladen door de client, deze code staat dus afgeschermd. Er is een basisfunctie connection die wacht op een connectie van een speler en deze dan met behulp van een socketvariabele de volgende multiplayer functies ter beschikking stelt:
8
addClient voegt een nieuwe speler toe aan een room en laat dit weten aan de andere speler in de room indien deze reeds aanwezig is waarna het spel gestart wordt. score zorgt voor het live updaten van de score tussen de spelers en stuurt daarnaast ook willekeurige obstakels naar alle spelers in de room. carPosition geeft bij een positiewissel van de wagen, de positie door aan de tegenstander. endGame laat aan de tegenstander weten dat de finish bereikt is door de speler en het spel afgelopen is. disconnect verwijdert de speler id van de server en verlaat de room. Daarnaast bevat het bestand nog enkele functies die niet door de socket variabele aangesproken. Deze functies zorgen voor het indelen van de rooms en het berekenen van de obstakels. Voor het indelen van de rooms wordt er gewerkt met logica in de functie checkRoom die de beschikbaarheid van de rooms nagaat en zo de spelers indeelt. Daarnaast is er nog de functie pushArray die een speler op de stack van een room plaatst bij het connecteren en de functie popArray die een speler verwijdert uit een room bij het disconnecteren.
Config files Procfile Dit bestand dient om Heroku te vertellen waar de web dyno dient gestart te worden. Een dyno is een proces binnen Heroku.
package.json In de package.json file vermelden we al de libraries die we nodig hebben, zodat de node.js package manager deze kan inladen tijdens het deployen. Ook staat hierin vermeldt waar de node.js server gestart moet worden. In dit geval in het server.js bestand, waar we de server instantie creëren.
Heroku Heroku is een PaaS (Platform as a Service), met andere woorden een platform gehost in de cloud. Cloud computing is de trend in de hedendaagse ICT wereld, dus hebben we ervoor gekozen om onze game ook niet zelf te hosten maar in de cloud. Mooi meegenomen is dat Heroku node.js ondersteunt en ook een eigen GIT repository heeft per applicatie, waarnaar je kan pushen en die wordt dan automatisch door Heroku gedeployed.
9
Libraries node.js node.js is een softwareplatform om javascript applicaties in te draaien. Node.js heeft een ingebouwde HTTP server wat het ideal maakt om server-side applicaties mee te draaien zonder gebruik te maken van een Apache server. Het is ook event gestuurd, wat het ideal maakt voor real-time applications zoals onze multiplayer game. In tegenstelling tot andere javascript applicaties worden node.js applicaties niet uitgevoerd door de web browser maar door de Google V8 javascript engine.
pixi.dev.js Pixi.js is een Web GL 2D Render javascript engine. Het ondersteunt gebruikersinteracties met Pixi objecten en is daarnaast een snel framework. Dat laatste was belangrijk voor onze game, dat de gebruiker niet te veel tijd zou verliezen met wachten op de weergaven van objecten in de game.
socket.io-1.0.4.js socket.io is een javascript library die er voor zorgt dat de server kan communiceren met verscheidene clients. Het client side gedeelte draait in de webbrowser, terwijl het server gedeelte in node.js draait. Via websockets wordt communiceren mogelijk gemaakt. Socket.io heeft ook ondersteuning voor rooms, wat ideaal is voor ons spelconcept. Daarnaast integreert het ook perfect met Express.js wat het de geschikte library voor ons project maakte.
express.js Zoals al eerder beschreven in het server-side.js gedeelte is express.js gebruikt in dit project voor de routing van de verschillende assets. Node.js zelf heeft geen echte methoden dus dienen allerlei frameworks gebruikt te worden die kunnen samenwerken met node.js. Express is er hier ééntje van. Aangezien we enkel routing nodig hadden, hadden we geen nood aan een complex framework. Geen een eenvoudig, snel framework.
howler.js Howler.js is een framework dat we gebruikt hebben voor ons geluid. We hebben gekozen voor deze library omdat het een performante library is zonder veel extra toeters en bellen, enkel hetgeen wij nodig hadden voor dit spel, namelijk geluid kunnen inladen, afspelen en stoppen. Daarnaast maakt deze library gebruik van HTML5 audio
Assets Images De afbeeldingen zijn allemaal .png of .gif bestanden, dit om de grote van de bestanden te beperken aangezien deze mobiel gedownload dienen te worden.
Sound Voor het geluid is er gekozen voor .mp3 en .ogg. We hebben gekozen voor deze twee formaten omdat de meeste smartphones deze ondersteunen. Daarnaast hebben we onze geluidsbestanden zo klein mogelijk gemaakt, zodat ze snel kunnen ingeladen worden en de game hierdoor niet vertraagd wordt.
10
Besluit Deze multiplayer tapgame is ontwikkeld met behulp van verschillende javascript framework. die voldeden aan de nodige vereisten van onze game. De game zelf is ontwikkeld op het node.js platform en wordt gehost op het Heroku platform. De connectie tussen twee spelers is mogelijk en de gebruikers kunnen met behulp van interacties acties ondernemen in het spel. Daarnaast is de game ook grafisch in retro stijl opgemaakt en voorzien van geluid. De speler die het snelst de progressbar vol laat lopen, die wint de game.
11
Referenties http://www.pixijs.com/ http://expressjs.com/ http://goldfirestudios.com/blog/104/howler.js-Modern-Web-Audio-Javascript-Library http://socket.io/ http://nodejs.org/ http://soundbible.com/ http://www.digitaltutors.com/tutorial/1719-Creating-a-Responsive-MultiplayerAction-Web-Game-in-HTML5 http://preloaders.net/
12