Array di oggetti: Bouncing Ball, parte 4

È finalmente arrivato il momento di mettere insieme quanto imparato nell’ultimo mese, programmazione ad oggettiarray, e di concludere il nostro esercizio Bouncing Ball. La domanda a cui oggi daremo, finalmente, risposta è sempre la stessa: come faccio a creare decine o centinaia di oggetti?

Un array di oggetti

Riprendiamo il codice dell’esercizio Bouncing Ball dove l’avevamo lasciato. Il primo passo è dichiarare un array di oggetti di tipo Ball.

La prima riga del nostro programma serviva per dichiarare una variabile di tipo Ball (il nostro oggetto) di nome myBall.

Ball myBall;

Sostituiamo questa riga di codice con un array di oggetti: per mantenere il mio codice chiaro e leggibile modifico anche il nome della variabile da myBall (che identifica un oggetto solo) a myBalls, utilizzando il plurale.

Ball[] myBalls;

Nella funzione setup() dove, precedentemente, inizializzavo l’oggetto ora devo dichiarare grandezza del mio array. Decido di avere a disposizione 100 oggetti:

myBalls = new Ball[100];

Non mi resta che inizializzare tutti gli oggetti richiamando il constructor all’interno della nostra classe, per sveltire, utilizzo un ciclo for:

for(int i = 0; i < myBalls.length; i++) {
 myBalls[i] = new Ball(random(width), random(height), random(0.5 ,5), random(0.5, 5));
 }

Debugging di un array di oggetti

Con l’ultima porzione di codice abbiamo riempito il nostro array occupando tutte e 100 le caselle con 100 oggetti differenti assegnando a ciascuno di essi, grazie alle funzioni random, la posizione X e Y e la velocità sull’asse orizzontale e verticale.

Se volessimo essere sicuri di averlo fatto nel modo corretto, potremmo pensare di utilizzare, come abbiamo sempre fatto finora, la funzione println(myBalls);

Debugging an Array in Processing

Come potete vedere nell’immagine, il testo riportato nella console non è per niente chiaro. Trattandosi di un array, proviamo a sostituire println con printArray:

Processing: using printArray

Un risultato leggermente migliore: vediamo che ci sono effettivamente 100 oggetti, numerati da 0 a 99, ma facciamo ancora fatica a capire se il contenuto del nostro array è corretto.

Accedere alle variabili interne agli oggetti

Nei nostri esercizi relativi agli oggetti abbiamo imparato a usare la dot notation per richiamare i metodi all’interno degli oggetti. La riga di codice myBall.display() serviva per richiamare la funzione display() contenuta nell’oggetto creato precedentemente.

Utilizzando lo stesso metodo possiamo accedere anche alle variabili interne all’oggetto: scrivendo, ad esempio, myBall.ellipseX; verrà restituito il valore di ellipseX.

Dal momento che, però, non stiamo lavorando con un singolo oggetto ma con un array abbiamo due possibilità:

  1. accedere ai dati di un oggetto specifico: println(myBalls[34].ellipseX);
  2. utilizzare la variabile contatore nel nostro ciclo for per visualizzarli tutti

Aggiungiamo questa riga di codice all’interno del nostro ciclo for:

println("["+i+"] - ellipseX: " + myBalls[i].ellipseX);

Ed ecco che il risultato è finalmente comprensibile:

Processing accessing variables in objects

Bouncing Balls, parte 4

Concludiamo modificando il codice contenuto in draw() per vedere tutte e cento le nostre sfere rimbalzare sullo schermo.

Ball[] myBalls;

void setup() {
  size(700, 500);
  myBalls = new Ball[100];
  for(int i = 0; i < myBalls.length; i++) {
    myBalls[i] = new Ball(random(width), random(height), random(0.5 ,5), random(0.5, 5));
  }  
}

void draw() {
  background(0);
  for(int i = 0; i < myBalls.length; i++) {
    myBalls[i].display();
    myBalls[i].move();
    myBalls[i].checkEdges();
  } 
}

Non ho incluso il codice della classe Ball perché non è stata toccata nemmeno una riga di codice. Se avete fatto tutto correttamente, il risultato sarà il seguente:

Bouncing Ball Final

Array: dichiarazione, inizializzazione e utilizzo

Avevo concluso l’ultimo post relativo alla programmazione orientata agli oggetti con una domanda: se volessimo creare decine o centinaia di oggetti? Il copia incolla non sarebbe certamente la scelta più consona.

Abbiamo capito già da tempo che uno dei principi di base della programmazione è scrivere meno righe di codice possibile per ottenere il risultato sperato. Introduciamo oggi il concetto di array.

Cos’è un Array?

Nel post dedicato alle variabili abbiamo parlato di come dichiararle e inizializzarle ma non ci siamo mai soffermati su una questione molto importante: una variabile può contenere un solo valore.

È vero che questo valore può variare in base all’andamento del nostro programma ma non è possibile, ad esempio, che una variabile di tipo integer possa valere contemporaneamente 5 e 10.

Gli array risolvono questo problema perché rappresentano una lista di valori: all’interno di un array io posso inserire una serie di valori, purché siano tutti dello stesso data type. Un altro importante vantaggio è che ciascun elemento ha un ordine preciso all’interno della lista che è identificato da un indice univoco.

È fondamentale ricordare che l’indice parte sempre da di conseguenza se sto utilizzando un array che contiene 10 elementi, l’ultimo valore avrà un indice pari a 9.

Ricapitolo per chiarezza i punti chiave:

  • Un array rappresenta una lista di elementi
  • Gli elementi all’interno dell’array devono essere tutti dello stesso tipo
  • Gli elementi all’interno dell’array hanno un ordine preciso
  • Ciascun elemento è identificato da un indice univoco che parte sempre da 0

Dichiarazione e creazione di un array

La sintassi per dichiarare un array è molto semplice: rispetto a quella utilizzata per le variabili, dobbiamo solo aggiungere delle parentesi quadre:

int[] myArray;

Con questa riga di codice ho dichiarato un array di nome myArray che conterrà al suo interno una lista di valori di tipo integer. Non ho ancora detto al mio programma quanti elementi conterrà questo array e, di conseguenza, la lista è ancora vuota.

Passiamo all’inizializzazione di myArray:

int[] myArray = new int[10];

Ho creato un’istanza di un nuovo array e ho gli ho assegnato una dimensione: la lista sarà composta da 10 elementi di tipo integer.

La grandezza dell’array può essere hard-coded come nell’esempio qui sopra, oppure può essere una variabile (di tipo integer) oppure un’espressione matematica che dia come risultato un integer:

// Un array di integer con grandezza hard-coded
int[] myArray = new int[10];

// Un array di float con grandezza passata tramite variabile (integer)
int sizeOfArray = 5;
float[] mySecondArray = new float[sizeOfArray];

// Un array di oggetti "Car" con grandezza passata tramite funzione
Car[] cars = new Car[2+5];

Inserire valori in un array

Non ci resta che cominciare a riempire il nostro array di informazioni.

Una prima opzione è quella di inserire le variabili specificando la posizione indice di riferimento. Questa scelta, ovviamente, non è la più funzionale.

// Dichiaro e inizializzo il mio array che conterrà 5 elementi di tipo integer
int[] myArray = new int[5];

myArray[0] = 5;   // Il primo elemento (indice = 0) sarà: 5
myArray[1] = 10;  // Il secondo elemento (indice = 1) sarà: 10
myArray[2] = 3;   // Il terzo elemento (indice = 2) sarà: 3
myArray[3] = 0;   // Il quarto elemento (indice = 3) sarà: 0
myArray[4] = 340; // Il quinto elemento (indice = 4) sarà: 340

Una seconda opzione è quella di inserire tutti i valori, separati da virgole, all’interno di parentesi graffe:

// Dichiaro un array di tipo integer di nome myArray con all'interno i seguenti valori: 5, 10, 3, 0, 340

int[] myArray = {5, 10, 3, 0, 340};

Le due porzioni di codice qui sopra rappresentato, di fatto, la stessa identica lista.

Operazioni matematiche con gli array

Per capire se tutto è chiaro, proviamo ad analizzare un problema: se volessimo creare una lista di 500 elementi contenenti numeri random compresi tra 0 e 255?

Per prima cosa non dobbiamo dimenticare che la funzione random() restituisce valori di tipo float. Avendo già deciso che la nostra lista sarà composta da 500 elementi, possiamo anche inizializzare il nostro array:

float valori[] = new float[500];

Ora dobbiamo inserire tutti i valori; farlo uno ad uno sarebbe un massacro ma, per fortuna, abbiamo già studiato i cicli while e for il cui scopo è effettuare operazioni ripetitive.

Per comodità utilizzerò un ciclo for: posso usare la variabile del ciclo per indicare l’indice dell’array nel quale inserire il valore random:

float valori[] = new float[500];

for(int i = 0; i < 500; i++) {
  valori[i] = random(255);
}

printArray(valori);

Array e ciclo for

Come si può notare nell’immagine, grazie alla funzione printArray nella console visualizzo tutti i valori e il relativo indice all’interno dell’array.

Ovviamente è possibile accedere singolarmente a qualsiasi elemento indicando l’indice:

println("Il valore all'indice 306 è: "+ valori[306]);

Debugging degli array e .length

Un errore in cui si può incorrere frequentemente quando si lavora con gli array è: ArrayIndexOutOfBoundsExceptions. Tale problema si verifica quando chiediamo al nostro programma di accedere a un valore ad un indice non presente nell’array.

Se modifichiamo il codice del ciclo for nel precedente esempio in questo modo:

for(int i = 0; i <= 500; i++)

riscontreremo proprio questo tipo di errore perché il ciclo si ripeterà finché il valore di i sarà uguale a 500. Il problema è che la grandezza dell’array che ho dichiarato all’inizio è pari a 500 ma, partendo da 0, l’ultimo valore dell’indice è 499.

Per ovviare a questo problema possiamo usare una proprietà degli array: .length. In pratica, anziché dover ricordarsi quant’è la lunghezza di ogni array e, prossimamente, vedremo che questa potrebbe anche variare, diciamo al programma di capire da sé quant’è la lunghezza della nostra lista e di comportarsi di conseguenza.

Ecco che il codice finito potrebbe essere:

float valori[] = new float[500];

for(int i = 0; i < valori.length; i++) {
  valori[i] = random(255);
}

printArray(valori);

Esercizio: Bouncing Ball, parte 3 (OOP)

Se vi siete persi gli articoli precedenti, qui trovate la parte 1 e la parte 2 di questo esercizio. Con questo post non aggiungeremo niente di nuovo al nostro programma ma rivedremo per intero il codice per trasformarlo in una versione ad oggetti.

Bouncing Ball Object Oriented

Per prima cosa aggiungiamo una nuova Tab che chiameremo Ball, come la classe che creeremo:

class Ball {
 
}

Per non dimenticarci il constructor, aggiungiamo subito la sintassi necessaria:

class Ball {
 Ball() {
 }
}

A questo punto facciamo copia-incolla delle porzioni di codice che, nel nostro sketch principale, riguardano la nostra palla: ricordatevi di portare nella classe sia le variabili che le funzioni.

Questo è il risultato finale:

class Ball {
  int ellipseX;
  int ellipseY;
  int speedX;
  int speedY;

  Ball() {
    ellipseX = 0;
    ellipseY = height/2;
    speedX = 1;
    speedY = 1;
  }

  void display() {
    ellipse(ellipseX, ellipseY, 50, 50);
  }

  void move() {
    ellipseX = ellipseX + speedX;
    ellipseY = ellipseY + speedY;
  }

  void checkEdges() {
    if (ellipseX > width || ellipseX < 0) {
      speedX = speedX * -1;
    }
    if (ellipseY > height || ellipseY < 0) {
      speedY = speedY * -1;
    }
  }
}
Ball myBall;

void setup() {
  size(700, 500);
  myBall = new Ball();
}

void draw() {
  background(0);
  myBall.display();
  myBall.move();
  myBall.checkEdges();
}

Aggiungiamo constructor

Miglioriamo la nostra classe creando dei nuovi constructor a cui possiamo passare delle variabili. Come già fatto nell'esempio del post precedente, conviene sostituire il data type delle variabili da integer float.

class Ball {
  // VARIABILI
  float ellipseX;
  float ellipseY;
  float speedX;
  float speedY;
  
  // CONSTRUCTOR
  Ball() {
    ellipseX = random(width);
    ellipseY = random(height);
    speedX = 1;
    speedY = 1;
  }
  
  Ball(float _ellipseX) {
    ellipseX = _ellipseX;
    ellipseY = random(height);
    speedX = 1;
    speedY = 1;
  }
  
  Ball(float _ellipseX, float _ellipseY) {
    ellipseX = _ellipseX;
    ellipseY = _ellipseY;
    speedX = 1;
    speedY = 1;
  }
  
  Ball(float _ellipseX, float _ellipseY, float _speedX) {
    ellipseX = _ellipseX;
    ellipseY = _ellipseY;
    speedX = _speedX;
    speedY = 1;
  }
  
  Ball(float _ellipseX, float _ellipseY, float _speedX, float _speedY) {
    ellipseX = _ellipseX;
    ellipseY = _ellipseY;
    speedX = _speedX;
    speedY = _speedY;
  }
  
  // METODI
  void display() {
    ellipse(ellipseX, ellipseY, 50, 50);
  }

  void move() {
    ellipseX = ellipseX + speedX;
    ellipseY = ellipseY + speedY;
  }

  void checkEdges() {
    if (ellipseX > width || ellipseX < 0) {
      speedX = speedX * -1;
    }
    if (ellipseY > height || ellipseY < 0) {
      speedY = speedY * -1;
    }
  }
}

A questo punto non ci resta che sperimentare se funziona tutto correttamente provando a passare delle variabili alla nostra classe:

myBall = new Ball(random(width), random(height), 5, 10);

OOP: Classi e oggetti, parte 2

Ripartiamo da dove ci siamo fermati con l’ultimo post: abbiamo scritto la nostra prima classe e abbiamo creato il nostro primo oggetto. Nell’approfondimento di oggi su classi e oggetti parleremo di alcune questioni importanti: come mantenere pulito il codice quando si lavora con le classi, come passare parametri al constructor e, infine, come creare n oggetti.

Operazione pulizia: utilizzare le Tab

Car myCar;

void setup() {
  size(500, 500);
  myCar = new Car();
}

void draw() {
  background(255);
  myCar.display();
  myCar.move();
}


class Car {
  // Dati
  int carLength;
  int xPos;
  int yPos;
  int speed;

  // Constructor
  Car() {
    carLength = 10;
    xPos = 0;
    yPos = 200;
    speed = 1;
  }

  // Metodi
  void display() {
    rectMode(CENTER);
    fill(0);
    rect(xPos, yPos, carLength, 10);
  }

  void move() {
    xPos = xPos + speed;
  }
}

Il nostro codice ora è lungo 40 righe in tutto e la classe Car occupa la maggior parte dello spazio. Immaginate per un secondo di avere tra le mani un programma molto complesso, che utilizza tante tipologie di oggetti differenti. Sarebbe davvero problematico andare a cercare di volta in volta scorrendo in su e in giù la porzione di codice che si riferisce all’oggetto che vogliamo utilizzare.

Per fortuna Processing viene in nostro aiuto con una funzione molto comoda: le Tab.

Crea una nuova Tab su Processing

Cliccando sulla freccia che punta in basso accanto al nome del nostro sketch, si aprirà un menu con diverse opzioni. Clicchiamo su New Tab (su Mac la scorciatoia è ⇧⌘N). A questo punto il programma ci chiederà il nome che vogliamo dare alla nostra tab; dovendo spostare lì la classe Car, utilizziamo lo stesso nome sempre con l’iniziale maiuscola.

Una volta cliccato su OK, l’editor creerà un altro file .pde che verrà aggiunto automaticamente alla cartella dove abbiamo salvato il nostro sketch (la scorciatoia per vedere velocemente la cartella è ⌘K).

Sketch Folder

A questo punto facciamo copia-incolla del codice della classe nella nuova tab e proviamo a cliccare Run. Il programma dovrebbe aprirsi e funzionare esattamente come prima con la differenza che, ora, il codice del file principale – quello nominato sketch_160115a.pde in questo esempio – è molto più breve e pulito.

Passare parametri al constructor

La nostra classe Car è ora funzionale e pulita ma è ancora migliorabile: ho scritto più volte dell’importanza di utilizzare le variabili invece di parametri hard-coded ma il constructor che abbiamo ora genererà sempre oggetti con le stesse caratteristiche: la lunghezza sarà sempre pari a 10 pixel, la posizione di partenza sarà x = 0 e y = 200 e la velocità pari a 1.

La buona notizia è che possiamo inserire nella nostra classe più constructor mantenendo una sintassi simile a quella che già conosciamo; dobbiamo solo aggiungere, all’interno della parentesi tonda, le variabili che decidiamo di passare ai nostri oggetti.

In questo primo esempio, aggiungiamo la possibilità di modificare la lunghezza della nostra macchina.

class Car {
  // Dati
  int carLength;
  int xPos;
  int yPos;
  int speed;

  // Constructor
  Car() {
    carLength = 10;
    xPos = 0;
    yPos = 200;
    speed = 1;
  }
  
  // Secondo Constructor
  Car(int tempCarLength) {
    carLength = tempCarLength;
    xPos = 0;
    yPos = 200;
    speed = 1;
  }

  // Metodi
  void display() {
    rectMode(CENTER);
    fill(0);
    rect(xPos, yPos, carLength, 10);
  }

  void move() {
    xPos = xPos + speed;
  }
}

In questa nuova versione ho aggiunto alla classe un nuovo constructor che accetta un solo parametro di tipo integer. Quando il parametro viene passato devo utilizzare una variabile temporanea, motivo per cui ho inserito la variabile tempCarLength. La variabile temporanea dovrà poi essere assegnata alla variabile relativa all’interno del costructor: carLength = tempCarLength.

Questo è il momento in cui è necessario diventare molto ordinati e scrupolosi con i nomi che diamo alle nostre variabili, il rischio è di complicarci inutilmente la vita.

Ora che abbiamo un nuovo constructor, torniamo al nostro sketch principale e modifichiamo l’inizializzazione da così:

myCar = new Car();

a così:

myCar = new Car(25);

Proviamo a lanciare il nostro sketch.

Variabili al constructor

Il nostro rettangolo sarà ora lungo 25 pixel.

Cosa succede se proviamo a passare al nostro constructor una variabile di tipo float?

myCar = new Car(25.7);

Ovviamente ci verrà restituito un errore: The constructor “Car(float)” does not exist.

Constructor, constructor, constructor

Il prossimo passaggio sarà creare tanti constructor quanti sono i dati che il nostro oggetto contiene: nel nostro programma vorremmo, ad esempio, decidere anche la posizione x e y di partenza oltre, ovviamente, diversi valori di velocità. Possiamo fare anche un passaggio in più: aggiungiamo una nuova variabile che contenga anche il colore della macchina.

Vi invito a provare a modificare la classe per conto vostro e di non guardare subito il codice qui sotto. In questo modo capirete se, effettivamente, vi è tutto chiaro.

class Car {
  // Dati
  int carLength;
  float xPos;
  int yPos;
  float speed;
  color c;

  // Constructor
  Car() {
    carLength = 10;
    xPos = 0;
    yPos = 200;
    speed = 1;
    c = color(0);
  }
  
  // Secondo Constructor
  Car(int tempCarLength) {
    carLength = tempCarLength;
    xPos = 0;
    yPos = 200;
    speed = 1;
    c = color(0);
  }
  
  // Secondo Constructor
  Car(int tempCarLength, int tempXPos) {
    carLength = tempCarLength;
    xPos = tempXPos;
    yPos = 200;
    speed = 1;
    c = color(0);
  }
  
  Car(int tempCarLength, int tempXPos, int tempYPos) {
    carLength = tempCarLength;
    xPos = tempXPos;
    yPos = tempYPos;
    speed = 1;
    c = color(0);
  }
  
  Car(int tempCarLength, int tempXPos, int tempYPos, float tempSpeed) {
    carLength = tempCarLength;
    xPos = tempXPos;
    yPos = tempYPos;
    speed = tempSpeed;
    c = color(0);
  }
  
  Car(int tempCarLength, int tempXPos, int tempYPos, float tempSpeed, color tempColor) {
    carLength = tempCarLength;
    xPos = tempXPos;
    yPos = tempYPos;
    speed = tempSpeed;
    c = tempColor;
  }

  // Metodi
  void display() {
    rectMode(CENTER);
    fill(c);
    rect(xPos, yPos, carLength, 10);
  }

  void move() {
    xPos = xPos + speed;
  }
}

Sottolineo un paio di cose:

  1. Ho aggiornato la nostra classe senza toccare il codice del nostro sketch principale. Questa è la potenza della programmazione orientata agli oggetti: modificare porzioni di codice senza andare a intaccare le altre parti che compongono il programma.
  2. Ho utilizzato per la prima volta il datatype color. Non è niente di difficile ma, dal momento che non ho intenzione di spiegare come funziona in questo post, vi invito a leggere il manuale.
  3. Ho cambiato il datatype di speed in float e sono stato costretto a cambiarlo anche per xPos dal momento che nella funzione move() i due parametri vengono sommati. Se avessi lasciato xPos come int, Processing mi avrebbe restituito un errore.

Ora che la nostra classe è completa, possiamo sbizzarrirci a passare quanti parametri vogliamo, l’importante è  ricordarsi l’ordine dei parametri e i tipi di dati accettati:

myCar = new Car(25, 50, 200, 0.5, color(255, 255, 0));

Il bello è che possiamo utilizzare anche delle funzioni per passare i parametri. Vi ricordate di random?

myCar = new Car(25, 50, 200, random(5), color(random(255), random(255), random(255)));

Creiamo oggetti

Nell’introduzione agli oggetti avevo specificato che un punto di forza dell’OOP è poter creare più istanze dello stesso oggetto. Come possiamo, dunque, creare 3 macchine?

Riprendiamo in mano il nostro sketch principale e aggiungiamo altre due variabili di tipo Car chiamate myCar2 e myCar3, in setup() creiamo due nuove istanze degli oggetti e, in draw() ci assicuriamo di richiamare, per ciascuna, i metodi display() e move().

Car myCar, myCar2, myCar3;

void setup() {
  size(500, 500);
  myCar = new Car(25, 50, 200, random(5), color(random(255), random(255), random(255)));
  myCar2 = new Car(int(random(5, 50)), int(random(width)), int(random(height)), random(5), color(random(255), random(255), random(255)));
  myCar3 = new Car(int(random(5, 50)), int(random(width)), int(random(height)), random(5), color(random(255), random(255), random(255)));
}

void draw() {
  background(255);
  myCar.display();
  myCar.move();
  
  myCar2.display();
  myCar2.move();
  
  myCar3.display();
  myCar3.move();
}

Premiamo su Run:

Abbiamo creato 3 oggetti Car

Fino a qui tutto bene, ma, a questo punto la domanda che sorge spontanea è: se volessimo creare decinecentinaia di oggetti? Dovremmo impazzire con il copia-incolla. Chiaramente esiste una soluzione più comoda: utilizzare gli array che, per l’appunto, saranno il prossimo argomento che tratteremo.

Compiti a casa

È arrivato il momento di dimostrare di aver capito come funziona la programmazione ad oggetti: riprendete in mano l’esercizio Bouncing Ball (parte 1 e parte 2) e modificate il codice per renderlo object-oriented.

OOP: Sintassi di classi e oggetti

La settimana scorsa abbiamo fatto un’introduzione generale alla programmazione orientata agli oggetti; con questo post entreremo nello specifico analizzando la sintassi corretta da utilizzare quando si scrive una classe e quando si usa un oggetto in Processing.

Sintassi della classe

Ricordo che per classe intendiamo il template di partenza che utilizzeremo per creare nuovi oggetti. Per creare una nuova classe abbiamo solo bisogno di assegnargli un nome ma, se vogliamo sfruttare al massimo l’OOP, ciascuna classe avrà le seguenti caratteristiche: nome, dati, constructor metodi.

class Car {
  // Dati
  int carLength;
  int xPos;
  int yPos;
  int speed;
  
  // Constructor
  Car() {
    carLength = 10;
    xPos = 0;
    yPos = 200;
    speed = 1;
  }
  
  // Metodi
  void display() {
    rectMode(CENTER);
    fill(0);
    rect(xPos, yPos, carLength, 10);
  }
  
  void move() {
    xPos = xPos + speed;
  }
}

Abbiamo scritto la nostra prima classe: Car. Per prima cosa sottolineo che le classi hanno sempre la prima lettera maiuscola: questo serve per differenziarle da tutti gli altri componenti che possiamo trovare in un programma (variabili, funzioni, ecc…).

Come potete vedere, la classe ha dei dati al suo interno che scriviamo sotto forma di variabili: la lunghezza dell’oggetto-macchina, la posizione X e Y di partenza e la velocità. Tali dati sono solo dichiarati ma non inizializzati. Per questo ci serve il constructor che è quella funzione speciale che serve per creare l’oggetto vero e proprio: per funzionare il constructor deve avere lo stesso nome della classe.

Nell’esempio qui sopra, ogni volta che creeremo l’oggetto Car gli assegneremo di default i dati del constructor quindi una lunghezza di 10 pixel, x = 0, y = 200 e velocità pari a 1.

Infine ci sono i metodi ovvero le funzionalità che vogliamo dare al nostro oggetto: in questo esempio la nostra macchina sarà visualizzata sullo schermo (display) e poi si muoverà (move).

Sintassi dell’oggetto

Car myCar;

void setup() {
  size(500, 500);
  myCar = new Car();
}

void draw() {
  background(255);
  myCar.display();
  myCar.move();
}

Nella prima riga stiamo dichiarando una variabile globale il cui nome è myCar il cui data-type non sarà uno dei classici incontrati finora (integer, float, stringa, ecc…) ma sarà un oggetto Car.

All’interno del blocco di setup() inizializziamo la nostra variabile myCar creando una nuova istanza dell’oggetto. In pratica con la sintassi myCar = new Car() stiamo richiamando il constructor presente all’interno della classe. Con una sola riga di codice abbiamo assegnato alla nostra myCar una serie di valori: carLength = 10, xPos = 0, yPos = 200; speed = 1 senza doverli riscrivere.

Nel ciclo draw() richiamiamo i metodi della classe. Da notare che a differenza delle funzioni che abbiamo visto fino ad ora, dobbiamo utilizzare la cosiddetta dot syntax ovvero myCar.display() perché display è un metodo presente all’interno di myCar. Se scrivessimo solo display(); chiaramente non funzionerebbe.

Il nostro primo programma ad oggetti

Car myCar;

void setup() {
  size(500, 500);
  myCar = new Car();
}

void draw() {
  background(255);
  myCar.display();
  myCar.move();
}


class Car {
  // Dati
  int carLength;
  int xPos;
  int yPos;
  int speed;

  // Constructor
  Car() {
    carLength = 10;
    xPos = 0;
    yPos = 200;
    speed = 1;
  }

  // Metodi
  void display() {
    rectMode(CENTER);
    fill(0);
    rect(xPos, yPos, carLength, 10);
  }

  void move() {
    xPos = xPos + speed;
  }
}

Ecco il codice completo del nostro programma. Potete notare come la classe costituisca un nuovo blocco di codice all’interno del programma. Il codice diventa più pulito e facile da leggere perché all’interno di setup e draw troviamo pochissime righe di codice. Copiando e incollando il codice e cliccando su Run vedrete un quadratino di 10x10px che si muoverà sullo schermo.

Mi rendo conto che in alcuni punti la spiegazione possa essere arzigogolata ma spero che vi sia tutto chiaro; potete provare a cambiare alcune porzioni di codice per verificare se tutto vi torna.