giovedì 1 agosto 2019

Post n. 376243dgfdy_x

Poche righe per dirvi che esistiamo ancora.

E anche il nostro progetto più ambizioso non è sparito. Emblemata continua ad esistere... e forse un giorno vedrà la luce.

Buona estate a tutti voi da Polpetta Games



lunedì 24 giugno 2019

Hello world - Unity - Code guidelines

Di seguito alcune considerazioni piu’ specifiche sul codice, unity e l’organizzazione del lavoro.

Stile, convenzioni indentazione

Legenda:

Da non fare assolutamente
Forse, ma meglio persarci bene
Ok

Pattern and practices


  • Variabili pubbliche (escluse readonly e const)
  • Iteratori a frame time (sono piu’ lenti e allocano, usate i for a frame time, possibilmente)
  • Over-Engineering (Unity e’ un framework ad altissimo livello. E’ molto raro che vi troviate a scrivere architetture complesse di componenti, almeno in una prima fase. Se sentite la necessita’ di usare tante classi da subito e strutture complesse, con buona probabilita’ esiste una soluzione piu’ semplice, magari gia’ implementata nel framework)
  • Statement e modificatori esotici: GOTO, ref & out parameters, tutte cose che rompono incapsulamento e OOP. Evitare!
  • Reflection: macigno sulle performance, inutile nel 99% dei casi (salvo che scriviate un IDE o un sistema di IoC). Vietatissima
  • [De]Allocare memoria a frame time (es. dentro update)
  • Confronti di stringhe usati come id: male male male. Laddove non indispensabile(tag di unity) usare enum o hashare le stringhe in interi.
  • Ereditarieta’: ci sono svariate ragioni per starci alla larga (o almeno non abusarne), in particolare (problemi serializzazione, male integrate in un sistema component based). Se sentite l’esigenza, cercate di capire le motivazioni alla base.
  • Interfacce: ottimo programmare contro le interfacce, ma molta attenzione perche’ unity non serializza. Ottimo usarle in contesti dove la serializzazione non e’ necessaria, altrimenti servono le dovute precauzioni.
  • Pattern Creazionali: per tutto quello che e’ in scena esistono i prefab. Spesso usare AbstractFactory o Builder e’ una complicazione inutile. I prefab sono Prototype. Può avere senso in determinati contesti, ma va dimostrato il reale vantaggio.
  • Linq: aumenta molto semplicità e la leggibilità del codice, MA alloca (see. immutable data structure). Usare con cautela a frame time, si in fase di inizializzazione oggetti
  • Delegates ed eventi: bellissimi usiamoli al posto di SendMessage, ma ricordatevi di deregistrarli. Attenzione quando entrano in gioco varianza e covarianza dei parametri, assicuratevi di avere capito bene.
  • Prefisso I davanti interfacce IQuestaEUnInterfaccia
  • Qualche commento (non troppi eventualmente Doxygen) dove vengono fatte scelte particolari non direttamente evidenti dal codice, TODO, ...
  • Nomi funzioni e classi significativi e autoesplicativi (meglio di troppi commenti)
  • Nomi funzioni, classi commenti in inglese
  • Property C# per getter/setter
  • Principle of Lest privilege
  • KISS

Unity Specific


  • Usare SendMessage(inefficente, no type safe)
  • Aggiornare la propria versione di unity prima di una decisione collettiva e relativi test
  • GameObject.Find a frame time
  • Destroy/Instantiate a frame time (privilegiate pool di oggetti pre-instanziati, attivate/disattivate)
  • Usare la cartella Resources senza valide ragioni
  • Proliferazione di branch nelle callback (Update/LateUpdate/FixedUpdate): per condizioni temporanee o sporadiche usare FSM o meglio coroutines.
  • Valori di ritorno di una coroutine che causano boxing: se non state scrivendo un sistema di estensioni delle coroutine dovreste avere solo yield return null o yield break.
  • Custom Inspector: decisamente si quando servono, MA attenzione a implementarli correttamente (controllate sempre Undo/Redo e che la serializzazione avvenga corretamente)
  • Prefab: assolutamente si quando servono, ma considerate che non esistono i nested prefab.
  • Serializzare reference ad altri oggetti della stessa scena (evita di scrivere codice in Awake/Start)
  • Caching di reference ad altri components o GameObject durante inizializzazione (Awake/Start): ottimo se non si puo’ serializzare la reference o in particolare contesti dove è più comodo
  • Modularizzazione componenti: Unity è component based. Il fatto che l’update dei vari behavior non abbiano un ordine prestabilito è una scelta di design precisa. Sottolinea come i vari oggetti devono essere per lo più indipendenti. Cercate di minimizzare le reference tra componenti/oggetti in maniera che ogni entità sia testabile il più possibile isolata dal contesto.
  • Scene di test: laddove unit testing siano improbabili (vd.gameplay) è comunque utile avere un modo semplice per verificare che un oggetto funzioni, testandolo in un contesto isolato. Es. creo un nuovo nemico e una scena di test semplificata che ne illustra comportamento. E’ utile agli altri a capire come funziona, a trovare bug, ad assicurarsi che dopo modifiche al codice funzioni ancora tutto. Non vale la pena per tutto ma certe volte fa comodo.

Ottimizzazioni


In linea di principio vale la regola di non ottimizzare se non sapete esattamente cosa state facendo. Quindi prima (e dopo)  interventi estremi, profilare prima di eventuali assunzioni ardite su cosa sia più o meno performante.

Alcune cose generiche di buon senso:

  • Minimizzare numero collider
  • Semplificare forme collider (sfere se non c’e’ ragione significativa per usare altro)
  • Minimizzare uso della fisica: per un moto rettilineo uniforme (o accelerato) senza collisioni scrivetevi il codice.
  • Ponderare il numero di GameObject
  • Semplificare matrice layer collisioni
  • Minimizzare raycast
  • Profilare  su device dopo ogni aggiunta/modifica non banale
  • Usare callback per operazioni saltuarie:
  • Branch negli shader
  • Allocazioni gratuite: specialmente a frame time. Evitate di dare lavoro al GC.

Usare Property getter all’interno della classe che dichiara il campo: una property equivale a una chiamata a metodo, se sono all’interno della stessa classe preferire l’accesso in lettura tramite la variabile.

Hello world - Unity - Asset import settings best practies

Models Import Settings


Negli import settings dei modelli ci sono diverse flag, ovviamente le opzioni variano da modello a modello. L’idea di massima e’ selezionare solo quelle che ci interessano ed evitare di importare qualsiasi cosa non necessaria. Di seguito alcuni esempi:




  • Read/Write enable: NO a meno che non serva modificare la mesh a runtime..
  • Import blendshapes: NO
  • Import Materials: NO unity importa i materiali nella stessa cartella dei modelli e genera disordine. Inoltre spesso non e’ in grado di scegliere shader e texture correttamente. Tanto vale farseli a mano cosi siamo sicuri di non sbagliare.
  • Generate Colliders: NO
  • Generate LM UVS: DIPENDE SE STATICI CON LIGHTMAP
  • Optimize Mesh: SI
  • Normals: meglio importarle se il modello le ha, altrimenti farle generare.. di norma servono sempre.
  • Tangents: se le ha importarle altrimenti generarle SOLO SE si usano Bumpmap o simili
  • Rig->Animation Type: sempre None se non e’ un modello animato, altrimenti Unity li crea automaticamente (animator, etc) uccidendo le performance.


WorkFlow


Qui alcune cose da tenere in considerazione sempre lavorando.


Console



La prima volta che un asset viene importato, se Unity non riconosce qualcosa sputa qualche warning o error nella console. Attenzione: una volta importato e clearata la console il warning non compare piu’ quindi poi va perso(salvo reimport forzoso e neanche sempre).

Ogni volta che importiamo un asset di qualsiasi tipo (script compresi), controlliamo la console. A volte i warning sono innocui (un modello di un cubo senza normali e’ plausibile, facciomole calcolare a unity) a volte possono tradursi in un errata importazione.


Save Project



Quando si modificano:


  • import settings
  • project settings
  • prefab
  • asset nel progetto (non istanze di asset in scena)


E’ molto probabile che sia necessario dire a unity di salvare il progetto: File->Save Project . A quello che ho capito flusha una cache dell’asset db. Se non si fanno certe modifiche non appaiono su git, quindi ve le tenete in locale a voi sembra che vada tutto, ma quando un altro scarica il progetto l’asset db e’ incosistente.


es.
  1. Modifico un settings su un prefab
  2. Non faccio save project e guardo git : mi dice che non ho modificato nulla
  3. Faccio save project e ora git vede che il file che stora le mie modifiche e’ effettivamente modificato
  4. Posso committare


GIT



Unity non si integra benissimo, alcune cose a cui stare attenti:


Asset modificati involontariamente o da Unity .



A volte succede che si modifichi un file senza volerlo o che Unity lo modifichi per qualche ragione oscura (es.line endings). Questo può creare conflitti o in ogni caso complicare la lettura della history del repo.


Best Practice: committare solo i file che siamo sicuri che di avere modificato. Inoltre tenere a mente quali file corrispondono a un tipo specifico di modifica.

Es. modifico un animation curve, che file devo committare? (in sto caso il meta relativo al modello su cui e’ esportata la clip)

Hello world - Rimozione delle superfici nascoste - Appunti

Rasterization -  HSR descrivere il problema ed elencare le strategie e quindi gli algoritmi

La computazione avviene in aritmetica intera, e le operazioni sono “per pixel” (pixel bound).

Non tutti i poligoni sopravvissuti, però, devono essere disegnati. Alcuni possono non essere visibili dall’osservatore perché nascosti (totalmente o parzialmente) da altri poligoni.

• Problema: dati un insieme di poligoni in 3D ed un punto di vista, si vogliono disegnare solo i poligoni visibili (o porzioni di essi).
Ogni poligono si assume essere piatto ed opaco.

• Vi sono essenzialmente due approcci:
– object-precision: l’algoritmo lavora sui poligoni stabilendo relazioni di occlusione reciproca. Il costo `e quadratico nel numero dei poligoni. Però la precisione `e elevata (precisione macchina).

– image-precision: l’algoritmo stabilisce occlusioni a livello del pixel. `E più veloce ma la precisione `e limitata.
• La rimozione delle superfici nascoste (Hidden Surface Removal, HSR) viene anche indicata come determinazione delle superfici visibili (Visible Surface Determination,VSD).

le strategie sono divise in due categorie:

1 - Object-precision

Back-face culling

• Non `e un algoritmo generale di HSR in senso stretto, ma solo una tecnica (object space) utile per eliminare poligoni ovviamente invisibili.
• L’eliminazione delle facce posteriori (o back-face culling), elimina i poligoni che, a causa della loro orientazione, non possono essere visti.
• Viene effettuato nello spazio vista.
• Se v `e la direzione di vista (punta verso l’osservatore) ed n `e la normale al poligono, è facile rendersi conto che il poligono `e visibile solo se:
n · v > 0
• Nota: se la scena `e composta da un solo solido convesso, il culling risolve anche il problema della eliminazione delle facce posteriori.

List-priority

• Gli algoritmi list-priority determinano un ordinamento per i poligoni che garantisce che l’immagine che si ottiene `e corretta se i poligoni vengono disegnati nell’ordine assegnato.
• Operano in object-precision (ovvero a livello di poligono), nello spazio 3D screen.
• Questa tecnica non scarta i triangoli ma li ordina.
• Infatti, risolve un problema di visibilità, ma implica il rendering back-to-front di tutti i triangoli.
• Pu`o costituire la fase di HSR dentro la rendering pipeline, oppure pu´o essere a carico della applicazione, nel qual caso si assume che l’HSR della pipeline sia “spento” e che i triangoli vengano disegnati nell’ordine in cui vengono spediti.

Depth-sort: algoritmo del pittore

– i poligoni vengono ordinati per profondit`a (o pseudo-profondità) decrescente.
– si effettua il rendering dei poligoni della lista, dal più lontano al più vicino (back-to-front rendering ).
– In questo modo i poligoni più lontani vengono sovrascritti da quelli più vicini.

• Problema 1: se gli z-intervalli dei due poligono non sono disgiunti l’algoritmo non si applica (cos’è la profondità del poligono?); bisogna controllare altre condizioni per stabilire se uno dei due può essere disegnato prima dell’altro.
• Problema 2: nel caso ciclico `e necessario spezzare i poligoni in parti.
• L’algoritmo di Newell risolve correttamente questi problemi.

Depth-sort con Newell

• Ordina i poligoni in base al vertice di massima distanza dall’osservatore.
• I poligoni i cui z-intervalli non si sovrappongono possono essere disegnati (in ordine back-to-front) come nell’algoritmo del pittore.
• Dati due poligoni P e Q i cui z-intervalli si sovrappongono, è corretto disegnare P prima di Q solo se almeno uno di questi test `e vero:
1. gli x-intervalli di P e Q sono disgiunte (veloce);
2. gli y-intervalli di P e Q sono disgiunte (veloce);
3. Si consideri il piano contenente Q. P `e contenuto completamente nel semispazio opposto a quello dove `e l’osservatore (abbastanza veloce)?
4. Si consideri il piano contenente P. Q è contenuto completamente nello stesso semispazio dove `e l’osservatore (abbastanza veloce)?
5. Le proiezioni di P e Q sullo schermo sono disgiunte (pesante)?
• Se tutti i test falliscono, controllo se `e corretto disegnare Q prima di P.
• Se anche questo fallisce Q viene tagliato usando il piano contenente P (con clipping), ed i pezzi vengono collocati nella lista al posto giusto.

BSP tree

• Poiché l’ordine dei poligoni dipende dal punto di vista, bisogna ricalcolarlo ad ogni spostamento.
• Non è praticabile in una applicazione interattiva (es: simulatore di volo).
• Usiamo un albero BSP per ottenere rapidamente l’ordine corretto dei poligoni al cambiare del punto di vista.
• Costruzione (off-line) di un albero BSP auto-partitioning (divido usando i piani che
contengono i poligoni). Le foglie sono poligoni o frazioni.
• Attraversando l’albero si ottiene l’ordinamento desiderato:
– consideriamo la radice dell’albero
– il punto di vista si trova (diciamo) a destra dell’iperpiano associato alla radice.
– allora i poligoni che stanno a sinistra si possono disegnare prima di quelli che stanno a destra dell’iperpiano.
– l’ordine per i poligoni che si trovano nei due semispazi si ottiene ricorsivamente considerando separatamente i due sottoalberi.
• Questa tecnica si basa sull’idea comune ad altre di pre-processare la scena usando strutture dati opportune per ridurre il tempo di rendering.

2 - Image-precision

Depth-Buffer

• `E un algoritmo image-precision, opera nello spazio 3D screen.
• Il depth-buffer o z-buffer `e una matrice (grande come l’immagine) che contiene, per ciascun pixel, il più piccolo valore di profondità (z) incontrato finora.
• Durante la scan-conversion, per ciascun poligono che viene processato:
– si calcola la profondità (z) dei punti interni con interpolazione della z dei vertici (con interpolazione scan-line, come il colore in Gouraud shading).
– se la z del pixel `e inferiore a quella contenuta nello z-buffer, allora la sua intensità viene scritta nell’immagine e la z viene viene aggiornata.
• Vantaggio: semplicità di implementazione
• Svantaggio: occupazione di memoria: servono almeno 20-32 bits per pixel per avere una discretizzazione accettabile delle profondità.

Scan-Line Algorithm

Area Subdivision

Hello world - Cornici ASCII

┌───┬───┐
│      │      │
├───┼───┤
│      │      │
└───┴───┘

╔═══╦═══╗
║      ║      ║
╠═══╬═══╣
║      ║      ║
╚═══╩═══╝


Hello world - Elenco Software & Tools


Ecco un piccolo elenco degli strumenti che utilizzo di solito:
  • CMake
  • Gimp
  • Git
  • Inkscape
  • LibreOffice
  • LyX
  • Notepad++
  • Skype
  • TortoiseGit
  • WinMerge
  • Visual Studio

E voi cosa usate?


Hello world - Formule Utili

Distanza tra due punti
//----------------------------------------------------------------------------
double Distance(Vect2d p1, Vect2d p2)
{
double dx = p2.x - p1.x;
double dy = p2.y - p1.y;

return sqrt((double)dx*dx + dy*dy);
};


Distanza tra un punto e un segmento
//----------------------------------------------------------------------------
double DistanceToLine(Vect2d lineP1, Vect2d lineP2, Vect2d point)
{
double normalLength = _hypot(lineP2.x - lineP1.x, lineP2.y - lineP1.y);
double distance = (double)((point.x - lineP1.x) * (lineP2.y - lineP1.y) - (point.y - lineP1.y) * (lineP2.x - lineP1.x)) / normalLength;
return abs(distance);
}