Spillet av Connect 4 bringer tilbake herre minner. Dette klassiske spillet har sikkert gitt et inntrykk av alle som har spilt det. I denne artikkelen skal vi lage en multiplayer-versjon av Connect 4 ved hjelp av Node.js og Socket.io.
Denne opplæringen antar at du har Node.js og npm installert. For å håndtere frontendavhengighetene bruker vi Bower til å hente pakken og Grunt for å administrere oppgaver. Åpne en terminal og installer Bower and Grunt globalt ved å utføre:
$ sudo npm installere -g bower grunt-cli
Merk: Grunt krever Node.js versjoner> = 0.8.0. På tidspunktet for denne artikkelen hadde Ubuntus repositorier en eldre versjon av Node. Sørg for at du bruker Chris Leas PPA hvis du er på Ubuntu. For andre distribusjoner / operativsystemer, se Node.js installasjonsdokumenter for å få den nyeste versjonen.
Med Bower og Grunt-cli installert, la oss lage en katalog for prosjektet og hente Twitter Bootstrap og Alertify.js (for å administrere varslingsvarsler) ved hjelp av Bower.
$ mkdir connect4 $ cd connect4 $ bower installer bootstrap alertify.js
La oss nå sette opp en katalog for å administrere våre egendefinerte eiendeler. Vi nevner det eiendeler
og lagre våre tilpassede Mindre og JavaScript-filer inni den.
$ mkdir -p eiendeler / javascript, stylesheets $ touch assets / javascript /frontend.js
eiendeler / stilark /styles.less
eiendeler / stilark / variables.less
For å betjene de kompilerte eiendelene, oppretter vi en katalog som heter statisk
med underkataloger kalt javascript
og stilark
.
$ mkdir -p statisk / javascript, stilark
Åpen eiendeler / stilark / styles.less
og importere variables.less
og de nødvendige Mindre filene fra bootstrap.
// Kjernevariabler og mixins @import "... / ... /bower_components/bootstrap/less/variables.less"; @import "... / ... /bower_components/bootstrap/less/mixins.less"; // Tilbakestill @import "... / ... /bower_components/bootstrap/less/normalize.less"; @import "... / ... /bower_components/bootstrap/less/print.less"; // Core CSS @ import "... / ... /bower_components/bootstrap/less/scaffolding.less"; @import "... / ... /bower_components/bootstrap/less/type.less"; @import "... / ... /bower_components/bootstrap/less/code.less"; @import "... / ... /bower_components/bootstrap/less/grid.less"; @import "... / ... /bower_components/bootstrap/less/tables.less"; @import "... / ... /bower_components/bootstrap/less/forms.less"; @import "... / ... /bower_components/bootstrap/less/buttons.less"; // Komponenter @import "... / ... /bower_components/bootstrap/less/component-animations.less"; @import "... / ... /bower_components/bootstrap/less/glyphicons.less"; @import "... / ... /bower_components/bootstrap/less/dropdowns.less"; @import "... / ... /bower_components/bootstrap/less/navbar.less"; @import "... / ... /bower_components/bootstrap/less/jumbotron.less"; @import "... / ... /bower_components/bootstrap/less/alerts.less"; @import "... / ... /bower_components/bootstrap/less/panels.less"; @import "... / ... /bower_components/bootstrap/less/wells.less"; // Utility klasser @ import "... / ... /bower_components/bootstrap/less/utilities.less"; @import "... / ... /bower_components/bootstrap/less/responsive-utilities.less"; // Egendefinerte variabler @import "variables.less"; // Alertify @import (mindre) "... / ... /bower_components/alertify.js/themes/alertify.core.css"; @import (mindre) "... / ... /bower_components/alertify.js/themes/alertify.default.css"; // Tilpassede stiler
Med det gjort, la oss sette opp Gruntfile.js
å kompilere de mindre filene i CSS og kombinere alle JavaScript-filene til en enkelt fil. Den grunnleggende strukturen til Gruntfile.js
fil med noen oppgaver, ser noe ut som dette:
// Gruntfile module.exports = funksjon (grunt) // Initialisering av konfigurasjonsobjektet grunt.initConfig (// Oppgave konfigurasjon mindre: // ..., konkat: // ..., se: // ... ); // Load plugins // Definer oppgaver;
Vi definerer tre oppgaver for å administrere eiendelene. Den første vil være å kompilere alle de mindre filene til CSS. Den andre vil være å sammenkoble alle JavaScript-filer til en og til slutt vil den siste oppgaven være å se filer for endringer. Klokkeoppgaven ville være standardoppgaven og kan drives av å skrive grynte
i prosjektrotten, når vi er ferdige med å konfigurere gruntfilen.
La oss sette opp en oppgave å kompilere alle Mindre filer til CSS-filer i statisk / stil
katalog.
mindre: development: options: compress: true,, filer: "./static/stylesheets/styles.css": "./assets/stylesheets/styles.less
",,
Fortsett å sette opp en annen oppgave for å konkatisere alle JS-filene til en.
concat: opsjoner: ';',, js: src: ['./bower_components/jquery/jquery.js', './bower_components/bootstrap/dist/js/bootstrap.js', '. /bower_components/alertify.js/lib/alertify.js ',' ./assets/javascript/frontend.js
'], dest:' ./static/javascript/frontend.js
',,,
Til slutt, la oss stille klokkeoppgaven til å se filene våre for endringer og utføre de nødvendige oppgavene på lagre.
se: js: files: ['./bower_components/jquery/jquery.js', './bower_components/bootstrap/dist/js/bootstrap.js', './bower_components/alertify.js/lib/alertify. js ',' ./assets/javascript/frontend.js
'], oppgaver: [' concat: js '], mindre: filer: [' ./assets/stylesheets/*.less '], oppgaver: [' mindre '],
Med det gjort, laster vi inn de nødvendige npm-pluginene og registrerer standardoppgaven.
// Last plugins grunt.loadNpmTasks ('grunt-contrib-concat'); grunt.loadNpmTasks ( 'grov-contrib-mindre'); grunt.loadNpmTasks ( 'grynt-contrib-watch'); // Definer oppgaver grunt.registerTask ('standard', ['watch']);
La oss fortsette å administrere backend-avhengigheter ved hjelp av npm. For dette prosjektet bruker vi Express-rammeverket med Jade Templating Engine og Socket.io. Installer avhengighetene lokalt ved å utføre følgende kommando:
$ npm installere ekspress jade socket.io async grunt grunt-contrib-concat grunt-contrib-less grunt-contrib-watch
Katalogstrukturen bør nå lignes på dette:
Nå som vi har vår avhengighet satt opp, er det på tide å fortsette å gjøre frontenden av spillet vårt.
La oss fortsette ved å opprette en fil som heter server.js
og server innhold ved hjelp av Express.
var express = kreve ('express'); var async = krever ('async'); var app = express () var io = krever ('socket.io'). listen (app.listen (8000)); app.use ('/ static', express.static (__ dirname + '/ static')); app.get ('/', funksjon (req, res) res.render ('index.jade');); app.get ('/ landingPage', funksjon (req, res) res.render ('landing.jade');); console.log ('Lytter på port 8000');
Vi bruker Jade Templating Engine til å administrere maler. Som standard ser Express etter visninger inne i visninger
katalogen. La oss lage visninger
katalog og opprett Jade-filer for layout, indeks og takksiden.
$ mkdir -p visninger $ touch visninger / layout.jade, index.jade, landing.jade
La oss deretter redigere utformingen av prosjektet vårt, indekssiden og destinasjonssiden (landing.jade
).
doktype html html (lang = "en") hodetittel Koble 4 link (rel = 'stylesheet', href = "static / stylesheets / styles.css") body #wrap nav.navbar.navbar- ) .container-fluid .navbar-header a.navbar-brand (href = '#') Koble 4 blokkinnhold #footer .container p.text-muted | Utviklet av | Gaurav Narula for Nettuts blokk javascript script (src = '/ socket.io/socket.io.js') skript (src = 'static / javascript /frontend.js
')
utvider layoutblokkinnhold .container .row .col-xs-3 .p1-score på 0 # board.col-xs-6 table.center-table .form-gruppelabel (for = "shareUrl"). col-sm- 3.control-label.share-label Del URL: .col-sm-9 input (type = 'tekst' ReadOnly) .form-kontroll .col-xs-3 .p2-score p 0
utvider layoutblokkinnhold .jumbotron .containerTakk skal du ha!
Takk for at du spilte! Vi håper du likte spillet!
blokkér javascript
Legg merke til at vi serverer socket.io.js
, selv om det ikke er definert hvor som helst i statisk
katalogen. Dette skyldes at socket.io
modulen administrerer automatisk serveringen avsocket.io.js
klientfil.
Nå som vi har HTML-oppsettet, la oss gå videre til å definere stilene. Vi begynner med å overskrive noen bootstrap-variabler med verdiene av vårt valg inni eiendeler / stilark / variables.less
.
@ body-bg: # F1F1F1; @ tekstfarge: # 717171; @ overskrift-farge: # 333; @ varemerker: # 468847; @ merkevaresuksess: # 3A87AD; @ varemerkevarsel: # FFC333; @ merkevarefare: # FB6B5B; @ navbar-default-bg: # 25313E; @ navbar-standard-farge: #ADBECE; @ navbar-standard-link-farge: @ navbar-standard-farge; @ navbar-default-link-hover-farge: # 333;
Deretter legger vi til noen egendefinerte stiler til styles.less
.
// Tilpassede stiler / * Sticky Footer * / html, kropp høyde: 100%; / * Wrapper for sideinnhold å presse ned footer * / #wrap min-høyde: 100%; høyde: auto; margin: 0 auto -60px; polstring: 0 0 60px; #footer høyde: 60px; bakgrunnsfarge: # 65BD77; > .container polstring-venstre: 15px; polstring-høyre: 15px; .container .text-muted margin: 20px 0; farge: #fff; // Grid table border-collapse: separate; border spacing: 10px 10px; tabell tr margin: 10px; tabell tr td bredde: 50px; høyde: 50px; grense: 1px solid # 3A87AD; .center-table margin: 0 auto! important; flyte: ingen! viktig; .p1-poengsum, .p2-score polstring: 185px 0; bredde: 50px; høyde: 50px; skriftstørrelse: 25px; linjehøyde: 50px; farge: #fff; tekst-align: center; .p1-score float: right; p bakgrunn: # FFC333; .strøm grense: 5px solid mørkere (# FFC333, 10%); .p2-poengsum p bakgrunn: # FB6B5B; .strøm grense: 5px solid mørkere (# FB6B5B, 10%); .share-label linjehøyde: 34px; tekstjustering: høyre;
Med det gjort, la oss legge til noen JavaScript-kode i eiendeler / javascript /
å opprette nettet og legge til frontend.js
data-rad
og data-kolonne
attributter med riktige verdier dynamisk.
$ (dokument) .ready (funksjon () for (var i = 0; i < 6; i++) $('#board table').append("); for(var j = 0; j < 7; j++) $('#board tr').last().append("); $('#board td').last().addClass('box').attr('data-row', i).attr('data-column', j); );
Det dekker frontend-oppsettet. La oss kompilere eiendommene og starte serveren.
$ grunt mindre concat: js $ node server.js
Hvis du har fulgt med, bør indekssiden se slik ut:
Tips: Kjør grynte
kommandoen på prosjektrotten på en separat terminal. Det ville kalle standardoppgaven som skjer med å se på. Dette vil konkatisere alle JS-filer eller kompilere alle Mindre filer på hver lagre.
Målet i Connect 4 er å koble fire påfølgende "blokker" enten horisontalt, vertikalt eller diagonalt. Socket.io tillater oss å lage rom
at klienter kan bli med. Tenk på dem som IRC-kanaler.
Vi bruker denne funksjonen i spillet, slik at bare to spillere kan være i et rom og rommet blir ødelagt når en av dem slutter. Vi lager et objekt som vil holde orden på alle rom og i sin tur alle spill-stater. La oss begynne med å skape en funksjon i server.js
å lage tilfeldige romnavn.
funksjon generateRoom (lengde) var haystack = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; var rom = "; for (var i = 0; jeg < length; i++) room += haystack.charAt(Math.floor(Math.random() * 62)); return room; ;
Funksjonen forventer lengden på tilfeldig romnavnet vi ønsker å generere. Romnavnet er generert ved å sammenkoble tilfeldige tegn fra høystakk
streng. La oss endre vår rute for indekssiden for å inkludere delingsadressen og opprette en ny rute for å betjene innholdet hvis et bestemt rom er åpnet.
app.get ('/', funksjon (req, res) share = generateRoom (6); res.render ('index.jade', shareURL: req.protocol + ': //' + req.get vert ") + req.path + dele, dele: dele);); app.get ('/: rom ([A-Za-z0-9] 6)', funksjon (req, res) share = req.params.room; res.render ('index.jade' shareURL: req.protocol + ': //' + req.get ('host') + '/' + del, del: del););
I ovennevnte kode genererer vi delings-IDen ved hjelp av generateRoom ()
funksjon definert tidligere og passere i delings-ID og URL som parametere til malen. Den andre ruten forventer en parameter som heter rom som er begrenset av et regulært uttrykk. Regex tillater en streng som bare inneholder alfanumeriske tegn, med lengde seks. Igjen passerer vi på shareURL
og id som parametere til malen. La oss legge til noen attributter til inngangselementet i indeksen vår, slik at vi får tilgang til dem i frontend.js
seinere.
input (type = 'tekst', datarom = del, navn = "shareUrl", verdi = shareURL ReadOnly) .form-kontroll
Deretter la vi redigere frontend.js
For å koble til Socket.io-serveren, bli med i rommet og tilordne noen egenskaper til gjeldende spiller.
var socket = io.connect ('localhost'); funksjon Spiller (rom, pid) this.room = room; this.pid = pid; var room = $ ('input'). data ('rom'); var spiller = ny spiller (rom, ","); socket.on ('connect', funksjon () socket.emit ('join', rom: rom);); socket.on ('assign', funksjon (data) player.color = data.color; player.pid = data.pid; if (player.pid == 1) $ ('.p1-score p'). addClass ('current'); else $ ('. p2-score p'). addClass ('nåværende'););
Legg merke til at vi opprettet et objekt som heter spiller
å referere til spilleren på klientsiden. Ved tilkobling kalles sluttbegivenheten på baksiden som inturn utsender tildelingen avgir på frontenden for å tilordne noen egenskaper til spilleren. Vi kan nå fortsette å definere koden i backend for å håndtere bli med
begivenhet.
// et objekt for å holde alle gamestates. Nøkkel angir rom id var spill = ; io.sockets.on ('connection', funksjon (socket) socket.on ('join', funksjon (data) hvis (data.room i spill) if (typeof spill [data.room] .player2! = "undefined") socket.emit ('leave'); return; socket.join (data.room); socket.set ('rom', data.rom); socket.set ('color', '# FB6B5B '); socket.set (' pid ', -1); spill [data.room] .player2 = socket // Sett motstandere socket.set (' opponent ', spill [data.room] .player1); spill [data .rom] .player1.set ('motstander', spill [data.room] .player2); // Sett sving socket.set ('turn', false); socket.get ('opponent', funksjon (feil, motstander ) opponent.set ('turn', sant);); socket.emit ('assign', pid: 2); annet socket.join (data.room); socket.set , data.room); socket.set ('color', '# FFC333'); socket.set ('pid', 1); socket.set ('turn', false); spill [data.room] = player1: socket, board: [[0,0,0,0,0,0,0], [0,0,0,0,0,0,0], [0,0,0,0,0, 0,0], [0,0,0,0,0,0], [0,0,0,0,0,0], [0,0,0,0,0,0, 0]],; socket.emit ('assign', pid: 1);););
Merk: Socket.io event handlers på backend bør legges inn i io.sockets.on ('forbindelse', funksjon (socket)
kode blokk. På samme måte bør hendelseshåndtereren og frontend-JavaScript-koden være inne i $ (dokument) .ready (funksjon ()
kode blokk.
I koden ovenfor definerte vi hendelsesbehandleren for bli med
hendelse som sendes ut av frontenden. Det kontrollerer om det gjeldende rommet allerede eksisterer, og om spilleren to ikke allerede er tildelt og i så fall tilordner gjeldende klient som spiller to. Ellers tilordner den gjeldende klienten som spiller en og initialiserer styret. Vi sender ut permisjon
Hendelse på frontend for klienter som forsøker å bli med i et pågående spill. Vi har også satt noen egenskaper på kontakten ved hjelp av socket.set ()
. Disse inkluderer rom id, farge, pid og sving variabelen. Egenskapene som er angitt på denne måten, kan hentes fra tilbakekallingen til socket.get ()
. Deretter legger vi til permisjon
hendelseshåndterer på frontenden.
socket.on ('leave', funksjon () window.location = '/ landingPage';);
De permisjon
Hendelsesbehandler omdirigerer klienten enkelt til destinasjonssiden. Vi fortsetter nå med å avgive et arrangement som varsler begge spillerne om spillet er klar til å begynne. La oss legge til noen kode til hvis
betingelse for å bli med på vår server-side.
Hvis (data.room i spill) // ... legger til koden som eksisterer // Melde spill [data.room] .player1.emit ('notify', connected: 1, turn: true); socket.emit ('notify', connected: 1, turn: false);
Vi må definere en gi beskjed
hendelse i frontend som omhandler varselet. Alert.js
gir en fin måte å håndtere alle meldingene på. La oss legge til varslingshendelen i frontend.js
.
socket.on ('notify', funksjon (data) if (data.connected == 1) if (data.turn) alertify.success ('Spillere koblet! Din tur'); ellers alertify.success Motstanderens tur '););
Tid til å prøve vår fremgang så langt. Start serveren lokalt og få tilgang til localhost og delingsadressen i to separate vinduer. Hvis du har fulgt med, bør du bli møtt med et varsel i nederste høyre hjørne, som vist på bildet nedenfor:
La oss nå fortsette å legge til kode som avgir en hendelse når blokkene klikkes. For denne delen må vi avgjøre om klikket ble laget av riktig spiller. Det er her sving
eiendom vi satt på stikkontakten ville komme til spill. Legg til følgende kode til frontend.js
.
$ ('.boks'). klikk (funksjon () // finn boksen for å slippe platen til var klikk = rad: $ (dette) .data ('rad'), kolonne: $ (dette) .data ('kolonne'); socket.emit ('klikk', klikk););
Koden ovenfor angir en hendelseshåndterer på alle tabellceller. En ting å merke seg er at rutenettet i Connect 4 ligner på å legge murstein i en vegg, det vil si at man ikke kan fylle et bestemt (rad, kolonne) par hvis (rad-1, kolonne) -paret ikke er fylt. Derfor må vi først få det (rad, kolonne) paret i cellen som ble klikket, og deretter utarbeide en måte å bestemme den faktiske cellen som skal fylles ut. Dette gjøres i backend, i tilfelle handler for klikk
.
socket.on ('klikk', funksjon (data) async.parallel ([socket.get.bind (this, 'turn'), socket.get.bind (denne motstanderen), socket.get.bind dette, 'rom'), socket.get.bind (dette, 'pid')], funksjon (feil, resultater) if (results [0]) socket.set ('turn', false); resultater [1 ] .set ('turn', true); var i = 5; mens (i> = 0) if (spill [resultater [2]]. bord [i] [data.column] == 0) break; i--; hvis (i> = 0 && data.column> = 0) spill [resultater [2]]. bord [i] [data.column] = resultater [3]; socket.get ', funksjon (feil, farge) socket.emit (' drop ', rad: i, kolonne: data.column, color: color); resultater [1] .emit (' drop ' kolonne: data.column, color: color);; ellers console.log ('Opponentens tur');););
Ovennevnte hendelsehandler bruker async-modulen til å hente sokkelegenskapene samtidig. Dette unngår nesting tilbakeringinger i etterfølgende bruk av socket.get ()
. De resultater
variabel er en matrise med elementer i samme rekkefølge som socket.get ()
samtaler. Resultatene [0]
, refererer derfor til sving
og så videre.
Når egenskapene er hentet, bytter vi svingene og finner ut (rad, kolonne) paret for å fylle. Vi gjør dette i løpet av runden ved å starte fra den nederste raden (rad fem) og bevege seg oppover til verdien av brettet i (rad, kolonne) er null (noe som betyr at den ikke har blitt spilt på). Vi tilordner deretter pid (enten en eller negativ) til elementet på brettet og avgir miste
arrangement på begge spillerne. La oss legge til miste
hendelseshåndterer på frontend.js
og introdusere en animasjon som gir oss en fallende effekt.
socket.on ('drop', funksjon (data) var row = 0; stopVal = setInterval (funksjon () hvis (rad == data.row) clearInterval (stopVal); fillBox (rad, data.column, data. farge); rad ++;, 25);); Funksjon fillBox (rad, kolonne, farge) $ ('[data-rad = "' + (rad-1) + '"] [data-kolonne = "' + kolonne + '" , '); $ (' [data-rad = "'+ rad +'"] [datakolonne = "'+ kolonne +'"] '). css (' bakgrunn ', farge);
Vi implementerer drop animasjonen ved hjelp av JavaScript setInterval ()
metode. Fra den øverste rad (rad null) fortsetter vi å ringe fillBox ()
i intervaller på 25 sekunder til verdien av rad
tilsvarer verdien av data.row
. De fillBox
funksjonen fjerner bakgrunnen til det forrige elementet, i samme kolonne og tilordner en bakgrunn til det nåværende elementet. Deretter kommer vi til crux av spillet, implementering av vinnende og tegneforhold. Vi vil dekke dette i backend.
// Hjelperfunksjon funksjon getPair (rad, kolonne, trinn) l = []; for (var i = 0; i < 4; i++) l.push([row, column]); row += step[0]; column += step[1]; return l; // a list to hold win cases var check = [];
Vi begynner med å definere en hjelperfunksjon som returnerer fire (rad, kolonne) par enten horisontalt, vertikalt eller diagonalt. Funksjonen forventer nåværende rad og kolonne og en matrise som bestemmer økningen i rad og kolonneverdier. For eksempel, et anrop til getPair (1,1, [1,1])
ville returnere [[1,1], [2,2], [3,3], [4,4]]
som skjer med riktig diagonal. På denne måten kan vi få respektive par ved å velge passende verdier for skritt
array. Vi har også erklært en liste for å beholde alle funksjonene som ser etter vinner. La oss begynne med å gå gjennom funksjonen som sjekker for vinner horisontalt og vertikalt.
check.push (funksjon check_horizontal (rom, rad, start kolonne, tilbakeringing) for (var i = 1; i < 5; i++) var count = 0; var column = startColumn + 1 - i; var columnEnd = startColumn + 4 - i; if(columnEnd > 6 || kolonne < 0) continue; var pairs = getPair(row, column, [0,1]); for(var j = column; j < columnEnd + 1; j++) count += games[room]['board'][row][j]; if(count == 4) callback(1, pairs); else if(count == -4) callback(2, pairs); ); check.push(function check_vertical(room, startRow, column, callback) for(var i = 1; i < 5; i++) var count = 0; var row = startRow + 1 - i; var rowEnd = startRow + 4 - i; if(rowEnd > 5 || rad < 0) continue; var pairs = getPair(row, column, [1,0]); for(var j = row; j < rowEnd + 1; j++) count += games[room]['board'][j][column]; if(count == 4) callback(1, pairs); else if(count == -4) callback(2, pairs); );
La oss gå gjennom funksjonen ovenfor trinn for trinn. Funksjonen forventer fire parametre for rom, rad, kolonne og suksess tilbakeringing. For å sjekke om en seier vandret, kan cellen som ble klikket på, bidra til en vinnende tilstand på maksimalt fire måter. For eksempel kan cellen ved (5, 3) resultere i en seier i en av følgende fire kombinasjoner: [[5,3], [5,4], [5,5], [5,6]], [[5,2], [5,3], [5,4], [5,5] ], [[5,1], [5,2], [5,3], [5,4]], [[5,0], [5,1], [5,2], [5, 3], [5,4]]
. Antall kombinasjoner kan være mindre for grensebetingelsene. Algoritmen ovenfor omhandler problemet ved hånden ved å beregne venstre mest kolonne (variabel kolonne
) og høyre mest kolonne (variabel columnEnd
) i hver av de fire mulige kombinasjonene.
Hvis den høyeste kolonnen er større enn seks, er den av rutenettet og passet kan hoppes over. Det samme vil bli gjort hvis venstre mest kolonne er mindre enn null. Men hvis kantsaker faller i nettet, beregner vi parene (rad, kolonne) ved hjelp av getPair ()
hjelperfunksjonen vi definerte tidligere, og fortsett deretter for å legge til verdiene av elementene på brettet. Husk at vi tildelte en verdi pluss en på brettet for spiller en og negativ for spilleren to. Derfor bør fire påfølgende celler av en spiller resultere i en telling på henholdsvis fire eller negative fire. Tilbakeringingen kalles i tilfelle en seier og er bestått to parametre, en for spilleren (enten en eller to) og den andre for de vinnende parene. Funksjonen som omhandler den vertikale kontrollen er ganske lik den horisontale, bortsett fra at den kontrollerer kantsakerne i rader i stedet for kolonner.
La oss fortsette å definere sjekker for venstre og høyre diagonaler.
check.push (funksjon check_leftDiagonal (rom, startRow, startColumn, tilbakeringing) for (var i = 1; i < 5; i++) var count = 0; var row = startRow + 1 - i; var rowEnd = startRow + 4 - i; var column = startColumn + 1 - i; var columnEnd = startColumn + 4 - i; if(column < 0 || columnEnd > 6 || rowEnd> 5 || rad < 0) continue; var pairs = getPair(row, column, [1,1]); for(var j = 0; j < pairs.length; j++) count += games[room]['board'][pairs[j][0]][pairs[j][1]]; if(count == 4) callback(1, pairs); else if(count == -4) callback(2, pairs); ); check.push(function check_rightDiagonal(room, startRow, startColumn, callback) for(var i = 1; i < 5; i++) var count = 0; var row = startRow + 1 - i; var rowEnd = startRow + 4 - i; var column = startColumn -1 + i; var columnEnd = startColumn - 4 + i; if(column < 0 || columnEnd > 6 || rowEnd> 5 || rad < 0) continue; var pairs = getPair(row, column, [1,-1]); for(var j = 0; j < pairs.length; j++) count += games[room]['board'][pairs[j][0]][pairs[j][1]]; if(count == 4) callback(1, pairs); else if(count == -4) callback(2, pairs); );
Kontrollene for diagonaler er ganske lik dem for horisontal og vertikal kontroll. Den eneste forskjellen er at i tilfelle diagonaler kontrollerer vi kantsaker for både rader og kolonner. Endelig definerer vi en funksjon for å sjekke for trekk.
// Funksjon for å sjekke for tegnefunksjon check_draw (rom, tilbakeringing) for (var val i spill [rom] ['board'] [0]) hvis (val == 0) tilbake; Ring tilbake();
Å sjekke for trekker er ganske trivielt. Uavgjort er åpenbart dersom alle cellene i toppraden er fylt og ingen har vunnet. Dermed utelukker vi uavgjort dersom noen av cellene i øverste rad ikke er spilt på og ringe tilbakekallingen ellers.
Med de vinnende og trekningsbetingelsene sortert, må vi nå bruke disse funksjonene i klikkhendelsen og avgi a tilbakestille
hendelse på frontenden for å betegne klientene på slutten av spillet. La oss redigere klikkhendelsen for å håndtere disse forholdene.
hvis (i> = 0 && data.column> = 0) / * Tidligere kode hoppet over * / var win = false; check.forEach (funksjon (metode) metode (resultater [2], jeg, data.column, funksjon (spiller, par) win = true; if (player == 1) spill [resultater [2]]. .emit ('reset', text: 'You Won!', 'inc': [1,0], høydepunkt: par); spill [resultater [2]]. player2.emit ('reset' : 'Du har mistet!', 'Inc': [1,0], høydepunkt: par); else spill [resultater [2]]. Player1.emit ('reset', text: 'You Lost!' , 'inc': [0,1], høydepunkt: par); spill [resultater [2]]. player2.emit ('reset', text: 'You Won!', 'inc': [0,1 ], høydepunkt: par); spill [resultater [2]]. bord = [[0,0,0,0,0,0,0], [0,0,0,0,0,0,0 ], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0]];);); hvis (seier) return; check_draw (resultater [2], funksjon () spill [resultater [2]]. bord = [[0,0,0,0,0,0,0], [0,0,0,0,0, 0,0], [0,0,0,0,0,0], [0,0,0,0,0,0], [0,0,0,0,0,0, 0], [0,0,0,0,0,0,0]]; io.sockets.in (resultater [2]). Emit ('reset', 'text': 'Game Drawn', 'inc ': [0,0];;
I koden ovenfor ser vi etter en seier horisontalt, vertikalt og diagonalt. I tilfelle en seier sender vi ut tilbakestille
Begivenhet på frontenden med en passende melding for begge spillerne. De fremheve
Eiendommen inneholder de vinnende parene og inc
Egenskapen angir stigningspoeng for begge spillerne. For eksempel, [1,0]
ville betegne økende spillerens poengsum med en og spillers to score med null.
La oss fortsette med å håndtere tilbakestille
hendelse på frontenden.
socket.on ('reset', funksjon (data) if (data.highlight) setTimeout (funksjon () data.highlight.forEach (funksjon (par) $ ('[data-rad = 0 + '"] [data-kolonne ="' + par [1] + '"]'). Css ('bakgrunnsfarge', '# 65BD77'););, 500); setTimeout funksjon () $ ('td'). css ('bakgrunnsfarge', ') alertify.confirm (data.text, funksjon (e) hvis (e) socket.emit (' fortsette '); else window.location = '/ landingPage';;; 1200) // Angi poeng p1 = parseInt ($ ('.p1-score p'). html ()) + data ['inc'] [0 ]; $ ('.p1-score p'). html (p1); p2 = parseInt ($ ('.p2-score p'). html ()) + data ['inc'] [1]; $ '.p2-score p'). html (p2););
I tilbakestille
Event Handler, vi markerer de vinnende parene etter 500ms. Årsaken til tidsforsinkelsen er å la slipp animasjonen slutte. Deretter tilbakestilles brettet i bakgrunnen, og dukker opp en varslingsbekreftelsesdialog med teksten som sendes fra baksiden. Hvis brukeren bestemmer seg for å fortsette, sender vi ut Fortsette
hendelse på server siden eller omdirigere klienten til destinasjonssiden. Vi fortsetter da for å øke spillerpoengene ved å øke dagens poengsum med de mottatte verdiene fra serveren.
Neste, la oss definere Fortsette
hendelse handler i backend.
socket.on ('fortsett', funksjon () socket.get ('turn', funksjon (feil, sving) socket.emit ('notify', connected: 1, turn: turn;); );
De Fortsette
hendelse handler er ganske grei. Vi sender meldingen om igjen, og spillet gjenopptas i frontenden.
Neste, la oss bestemme hva som skjer når en av spillerne blir frakoblet. I dette scenariet bør vi omdirigere den andre spilleren til destinasjonssiden og fjerne rommet fra spillestedet. La oss legge til denne funksjonen i backend.
socket.on ('disconnect', funksjon () console.log ('Disconnected'); socket.get ('rom', funksjon (feil, rom) io.sockets.in ); hvis (rom i spill) slett spill.rom;;;);
Ovennevnte hendelsehandler ville kringkaste permisjon
arrangement til de andre spillerne og fjern rommet fra spillets objekt, hvis det fortsatt eksisterer.
Vi har dekket ganske mye grunnlag i denne opplæringen, og starter med å få avhengighetene, skape noen oppgaver, bygge front og back-end og slutte med et ferdig spill. Med det sagt, antar jeg det er på tide for dere å ha noen runder med vennene dine! Jeg vil gjerne svare på dine spørsmål i kommentarene. Ta gjerne på min repo på GitHub og improvisér på koden. Det var alt folkens!