Salve a tutti,
questo articolo sul webgl tratterà un argomento assai importante per programmare un eventuale videogioco: gli user input, cioè studieremo i metodi base per “raccogliere” gli input dell’utente ed elaborarli per fare variare la nostra scena. Al momento ci occuperemo dei due tipi di input più utilizzati dalle applicazioni grafiche: i Keyboard Input ( tastiera) e i Mouse Input.Prenderemo come esempio il classico cubo 3d dell’articolo precedente, mostreremo come la rotazione varierà a seconda degli input che gli verranno dati.


Scarichiamo l’esempio dal solito repository Lab (lesson05 ) e apriamo la relativa pagina html.

ora vediamo come verrà modificata la funzione start:

function Start() {
var canvas = document.getElementById(“Mycanvas”);
initGL(canvas);
initShaders();
initBuffers();
initTexture();
setInterval(tick, 15);

/*KeyBoard Input*/


document.onkeydown =KeyboardDown;
document.onkeyup = KeyboardUp;


/*Mouse Input*/


canvas.onmousemove = MouseMove;
canvas.onmousedown = ClickMouseDown;
canvas.onmouseup = ClickMouseUp;

}

Queste 5 funzioni javascript saranno quelle che verrano evocate nel caso l’utente esegua una delle seguenti azioni:

Azione Funzione chiamata
premere ( press down) uno dei pulsanti della tastiera KeyboardDown
rilasciare( press up) uno dei pulsanti della tastiera KeyboardUp
cliccare un pulsante del mouse ClickMouseDown
rilasciare un pulsante del mouse ClickMouseUp
spostare l’icona del mouse MouseMove

Possiamo notare un paio di cose: in questo momento stiamo registrando le funzioni che dovranno partire a seconda dell’operazione eseguite. Potete notare che per gli input da tastiera definiamo una proprietà del document, mentre per il mouse optiamo per definire un attributo del canvas: questo indica anche l’area e/o l’ambito in cui deve scattare una proprietà: nel caso della tastiera, noi vogliamo che OGNI volta che l’utente clicca sull freccette direzionali, il cubo deve ruotare, con il mouse invece vogliamo che tutti gli eventi tra cui click e spostamento del cursore, siano rilevanti solo dentro l’area del canvas quindi solo dentro la nostra scena.Perciò, se volessimo raccogliere gli eventi del mouse anche fuori il canvas, dovremmo mettere document come elemento da gestire.

KEYBOARD INPUT

Facciamo una piccola aggiunta alla funzione tick():

function tick()
{

KeyPressed();

drawScene();
}

Questa funzione ha un ruolo assai specifico: dovrà controllare infatti quale pulsante è stato schiacciato dalla tastiera ed abiliterà una rotazione del nostro cubo. Qualcuno potrebbe confondere questa funzione con la KeyboardDown e la KeyboardUp ma questo è il metodo più semplice per gestire un input continuo.

Facciamo un esempio: l’utente vuole ruotare il cubo verso destra, così clicca il pulsante-freccia adatto. Tale azione provoca la chiamata alla KeyboardDown e questo dovrebbe modificare l’angolo di rotazione di una quantità prefissata; e se l’utente volesse far ruotare l’oggetto a 360 gradi? Se non usassimo la KeyPressed(), l’utente sarebbe costretto a rilasciare e rischiacciare continuamente il pulsante (infatti l’evento di onkeydown parte solo quando si schiaccia il pulsante) . Invece, utilizzando questa funzione dentro la routine, all’utente basterà tenere premuto per fare un giro completo.Riassumendo:

KeyboardDown salvo pulsante schiacciato
Routine(KeyPressed) se il pulsante è schiacciato, vario angolo
KeyboardUp resetto pulsante schiacciato

Guardando il codice capiremo subito:

var currentPressedKey= Object();
function KeyboardDown(event)
{
currentPressedKey = event.keyCode;
}

function KeyboardUp(event)
{
currentPressedKey = null;
}

function KeyPressed()
{
if(currentPressedKey!=null)
{
switch(currentPressedKey)
{
case 37:
//freccia a sinistra
horizontalrotation+=1.0;
break;

case 38:
//freccia in alto
verticallrotation+=1.0;
break;

case 39:
//freccia a destra
horizontalrotation-=1.0;
break;

case 40:
//freccia in basso
verticallrotation-=1.0;
break;

}
}
}

La variabile globale currentPressedKey viene usata per registrare quale pulsante è stato cliccato: più precisamente ci salviamo il keyCode, che sarebbe il codice javascript con cui identifichiamo univocamente un tasto della nostra keyboard: per conoscere i valori degli altri pulsanti, potete utilizzare la tabella che trovere nel link seguente:
tabella code javascript

verticallrotation e horizontalrotation rappresentano gli angoli di rotazione del nostro cubo ( ovviamente il primo è l’angolo verticale e il secondo orizzontale).

MOUSE INPUT
Ora analizzeremo le funzioni che verranno chiamate nel caso l’utente usi il mouse:

var mouseClicked = false;
var oldX = null;
var oldY = null;
var newX=null;
var newY=null;
function ClickMouseDown(event)
{
mouseClicked= true;
oldX = event.clientX;
oldY = event.clientY;
}

function ClickMouseUp(event)
{
mouseClicked = false;
}

function MouseMove(event)
{
if (mouseClicked==true) {
newX = event.clientX;
newY = event.clientY;
horizontalrotation=horizontalrotation+newX – oldX;
verticalrotation=verticalrotation+newY – oldY ;
oldX = newX
oldY = newY;
}

}

La funzione ClickMouseDown salva una variabile booleana che indica se è stato schiacciato un pulsante e le coordinate X-Y del cursore del mouse. Ogni movimento del mouse scatena la funzione MoveMouse ( questo ci semplifica la vita e non siamo costretti a creare alcuna funzione dentro la routine tick): quest’ultima , nel caso il pulsante del mouse è ancora pressato, causa una variazione dei due angoli proporzionale allo spostamento registrato dal cambio di coordinate.ClickMouseUp non fa altro che resettare la variabile booleana di controllo.

L’ultima piccola modifica riguarda la funzione drawScene:

var horizontalrotation=0.0;
var verticalrotation=0.0;
function drawScene() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
loadIdentity();
mvTranslate([0.0, 0.0, -5.0]);

mvRotate(horizontalrotation,[0,1,0]);
mvRotate(verticalrotation,[1,0,0]);
gl.bindBuffer(gl.ARRAY_BUFFER, cubover);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubover.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, cuboTexture);
gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, cuboTexture.itemSize, gl.FLOAT, false, 0, 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, newTexture);
gl.uniform1i(shaderProgram.Texture, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cuboindex);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, cuboindex.numItems, gl.UNSIGNED_SHORT, 0);
}

Le uniche aggiunte sono le due funzioni per ruotare il cubo orizzontalmente e verticalmente. Salvate il file e testate gli input: se tutto funziona correttamente, dovreste riuscire a ruotare il cubo sia con le frecce della tastiera sia tenendo premuto il pulsante del mouse e spostando il cursore.

Per potere vedere l’esempio appena mostrato e scaricare il codice che è stato presentato nell’articolo, collegarsi al solito repository Lab .

Grazie per l’attenzione,

Andrea