In forma
È giunto il momento di sperimentare anche i comandi vocali.
L'API in questione è SpeechRecognition ma per quest'app ho deciso di utilizzare la libreria annyang che semplifica il lavoro di riconoscimento.
Ecco la consueta scheda di riepilogo:
In forma
- Url
- https://informa.figherie.it/
- Github
- https://github.com/polesello/informa
- Data di uscita
- 1 maggio 2024
- Caratteristiche
- SpeechRecognition, transizioni CSS, webBeacon
- Dispositivi
- Android, iOS, Desktop
- Browser
- Chrome, Firefox, Safari
Descrizione del gioco
Il gioco consiste nel dover indovinare una forma geometrica misteriosa che ha diverse caratteristiche indipendenti. Può essere un cerchio o un quadrato, di colore bianco o nero, di dimensioni grande o piccola e così via. Man mano che si sale di livello venne aggiunta una caratteristica, fino ad arrivare al massimo livello, il numero 12.
Numero di giocatori
Sempre nell'ottica di creare giochi che favoriscano la socialità, in questo caso si possono affrontare insieme fino a quattro giocatori, e lo schermo viene suddiviso in parti uguali a seconda del numero di giocatori.
Definirei anche questo un gioco da tavolo, ho quindi disposto i giocatori immaginando che siano seduti attorno a un tavolo, in particolare nella versione giocatori c'è anche il giocatore B che siede a capotavola.
Tutti i campi da gioco sono già presenti nell'HTML, al momento di scegliere il numero di giocatori viene impostato un attributo custom chiamato data-n e il css provvede a far sparire, ridimensionare e ruotare i vari campi da gioco.
Di seguito il codice di esempio per la versione di default, quella a due giocatori.
HTML
<!-- tavolo da gioco per massimo 4 giocatori -->
<div id="board" data-n="2" class="fullscreen hidden">
<div class="player">
<div class="field">
<div class="shape"></div>
</div>
<div class="name">A</div>
<span class="score">0</span>
</div>
<div class="player">
<div class="name">B</div>
<div class="field">
<div class="shape"></div>
</div>
<span class="score">0</span>
</div>
<div class="player">
<div class="name">C</div>
<div class="field">
<div class="shape"></div>
</div>
<span class="score">0</span>
</div>
<div class="player">
<div class="name">D</div>
<div class="field">
<div class="shape"></div>
</div>
<span class="score">0</span>
</div>
<button class="btn" id="btn-reset">↺</button>
</div>
CSS
/* 2 players */
#board[data-n="2"] .player:nth-child(n+3) {
display: none;
}
#board[data-n="2"] .player {
height: 50%;
}
#board[data-n="2"] .player:nth-child(1) {
transform: rotate(180deg);
border-top: 2px solid #edbec5;
}
Comandi vocali
La libreria annyang permette di definire i comandi (io ho usato singole parole ma è in grado di riconoscere anche frasi) come un oggetto che ha per chiave la frase riconosciuta e per valore la funzione da eseguire; quasi tutti i comandi riconosciuti si limitano ad aggiungere una classe CSS alla forma del giocatore attivo e a toglierne un'altra.
const commands = {
'piccolo': function() {
setClasses('small', 'large')
},
'grande': function() {
setClasses('large', 'small')
},
'bianco': function() {
setClasses('white', 'black')
},
'nero': function() {
setClasses('black', 'white')
},
'rotondo': function() {
setClasses('circle', 'square')
},
// accetto anche "cerchio"
'cerchio': function() {
setClasses('circle', 'square')
},
'quadrato': function() {
setClasses('square', 'circle')
},
...
Comandi speciali
Se mai qualche sviluppatore arriverà a leggere queste righe, sarà fiero di essere sviluppatore web e non semplice utilizzatore di giochini, perché potrà giocare a "In forma" con i 4 effetti speciali.
L'idea mi è venuta in qualche momento di disperazione, quando i comandi vocali non venivano riconosciuti. Immaginavo quindi voi, delusi utilizzatori di questo giochino, a imprecare contro la tecnologia, lo smartphone, la rete, Google e, non ultimo, Nello Polesello che vi ha proposto questo gioco.
Il codice che segue mostra un utilizzo avanzato di annyang che permette di dare un senso allo studio delle espressioni regolari e valorizza una comune imprecazione veneta, permettendo anche qualche variante.
Analizziamo la regexp /(vacca|mad?re).+(vacca|mad?re)/
Innanzitutto si noti che è una regexp e non una stringa, essendo delimitata dallo slash (/)
Di fatto riconosce qualunque frase che contiene le parole vacca e madre (in qualsiasi ordine) permettendo anche la variante mare, tipico termine veneto per indicare la madre. Potete quindi pronunciare:
- to mare vacca
- quella vacca de to mare
- tua madre è davvero un gran vacca
- la mia vacca è andata al mercato con tua madre
- la vacca è andata al mare
Risultato

'to mare vacca': {regexp: /(vacca|mad?re).+(vacca|mad?re)/, callback: function() {
document.getElementById('vitello').classList.add('show')
setTimeout(() => {
document.getElementById('vitello').classList.remove('show')
}, 5000)
}},
Apparizione degli effetti speciali
I quattro comandi nascosti fanno entrare delle immagini dai quattro lati dello schermo.
Al caricamento della pagina le immagini vengono posizionate appena fuori dal viewport, e grazie all'onnipresente proprietà CSS transition è sufficiente aggiungere (e togliere dopo 5 secondi) la classe show per far entrare dal lato giusto e poi far scomparire l'immagine desiderata.
#comparse #vitello {
transform: translateX(110vw)
}
#comparse #rocco {
transform: translateX(-110vw);
}
#comparse #gesu {
transform: translateY(-110vh);
}
#comparse img.show {
transform: translateX(0) !important;
}
Filtro anti oscenità
Una caratteristica che non mi aspettavo del riconoscimento vocale è il filtro anti-oscenità (profanity filter), che nel caso venga pronunciata una parola che non sta bene dire in contesti formali la restituisce censurata, ossia con tutte le lettere tranne la prima sostituite con il carattere asterisco (*)
Ecco perché l'augurio più scambiato al mondo dopo il Buon Natale, è stato indicato come l'invito ad andare in f***
Salire di livello
Per rendere il gioco coinvolgente e spingere a giocare più volte, ho creato 12 livelli in totale, ma solo i primi quattro sono attivi appena si comincia il gioco. Ogni volta che viene completato con successo un livelloviene attivato quello successivo, e viene memorizzato nel localStorage, in modo che sia persistente alla prossima apertura dell'app.
Volevo infine conoscere quanti di voi arrivano ai livelli più difficili, ma senza utilizzare cookie, sistemi di statistiche o salvataggi lato backend.
Ho quindi utilizzato la funzione navigator.sendBeacon inviando una richiesta al server dove passo come parametro in GET il parametro level. Lato server non succede nulla e non viene salvato niente, anzi, si ottiene una risposta HTTP 405 (Method not allowed) ma intanto viene salvato nel file di log di nginx un url del tipo index.html?level=9 e questo è quello che mi basta per capire che qualcuno ha superato il livello 9.
if (currentLevel == maxLevel && currentLevel < Object.keys(FEATURES).length) {
maxLevel++
localStorage.setItem('maxLevel', maxLevel)
initAvailableLevels()
// Invio al server il livello raggiunto per aver traccia nei log
if ('sendBeacon' in navigator) {
navigator.sendBeacon('?level='+ currentLevel)
}
}
Anche da desktop
Stavolta il gioco è compatibile anche da pc desktop.
Anzi, nella schermata iniziale anziché un mouse vi regalo un pennarello rosso per scegliere le opzioni di gioco.
Tutto grazie alla proprietà cursor, in cui è fondamentale specificare il punto attivo del puntatore (0 128), ché il pennarello scrive con la punta, mica col di dietro.
#intro {
padding: 20px;
cursor: url('img/pen-cursor.png') 0 128, auto;
}