GreenLab è un progetto di minigolf 3D sviluppato in Unity 6, con controlli touch e mouse, due percorsi cittadini, modalità solitaria, avversario IA e multiplayer online in stanze private. Il titolo visualizzato nel menu è PAR CITY; il nome del progetto Unity e dell'applicazione nelle Player Settings è GreenLab.
L'obiettivo è portare la pallina in buca usando il minor numero di colpi possibile. Il giocatore sceglie direzione e potenza attraverso un trascinamento, sfrutta rimbalzi e ostacoli e confronta il risultato con il par del percorso. Nelle modalità con più partecipanti, chi termina può osservare gli altri fino alla schermata dei risultati.
Questo README descrive il codice, gli asset e le configurazioni presenti nel repository. Le spiegazioni delle scelte progettuali ne illustrano il funzionamento, i vantaggi e i compromessi osservabili nell'implementazione; non costituiscono una ricostruzione di motivazioni storiche non documentate. I valori salvati in scene, prefab e ScriptableObject possono differire dai valori iniziali dichiarati negli script.
- Caratteristiche e modalità di gioco
- Controlli e ciclo di una partita
- Tecnologie e piattaforme
- Avvio del progetto
- Scene, percorsi e struttura del repository
- Architettura e scelte progettuali
- Tiro, fisica e recupero della pallina
- Avversario IA
- Multiplayer online
- Interfaccia, camera e spettatore
- Audio FMOD
- Grafica e profili di qualità
- Build e strumenti di sviluppo
- Estendere il progetto
- Verifica e limiti attuali
- Asset di terze parti e licenze
| Modalità | Comportamento |
|---|---|
| Solo | Un giocatore affronta il percorso, riceve il risultato rispetto al par e torna alla selezione della mappa. |
| IA | Il giocatore affronta il percorso insieme a una pallina controllata da una macchina a stati e da waypoint. A completamento di tutti i partecipanti viene mostrato un riepilogo dei colpi. |
| Host | Crea una stanza Photon privata, mostra il codice di accesso e sceglie la mappa per tutti. |
| Client | Entra nella stanza con il codice e attende che il Master Client avvii il percorso. |
Il multiplayer prevede fino a 4 partecipanti con la configurazione predefinita. Ogni giocatore controlla la propria pallina; non è implementata una gestione a turni. L'IA è generata soltanto nella modalità locale dedicata.
Le funzionalità principali comprendono:
- Tiro mediante trascinamento, linea di mira, indicazione della disponibilità al tiro e barra di potenza.
- Camera orbitale e indicatore della posizione della buca sullo schermo.
- Conteggio dei colpi e risultati golfistici: Hole in One, Albatross, Eagle, Birdie, Par e Bogey.
- Ostacoli modulari, rampe, tubi, imbuto, spirale e loop, distribuiti nei percorsi.
- Recupero della pallina fuori campo nella posizione precedente al tiro.
- Modalità spettatore locale e online, con cambio del bersaglio osservato.
- Menu principale, selezione della modalità e della mappa, impostazioni e conferma di uscita.
- Sfondo video del menu con immagine di attesa durante la preparazione.
- Audio FMOD per musica, ambiente, tiro, applauso e interazione con i pulsanti.
- Volumi Master, Music, Ambience ed Effects salvati localmente.
- Strumenti Unity Editor per configurare menu e prefab di rete e verificare le stanze Photon.
| Azione | Touch | Mouse |
|---|---|---|
| Preparare un tiro | Toccare la pallina ferma e trascinare | Premere il pulsante sinistro sulla pallina ferma e trascinare |
| Scegliere la direzione | Trascinare dalla pallina verso il lato opposto alla direzione desiderata | Stesso gesto con il mouse |
| Eseguire il tiro | Rilasciare il dito | Rilasciare il pulsante sinistro |
| Ruotare la camera | Trascinare fuori dalla pallina | Trascinare con il pulsante sinistro fuori dalla pallina |
| Cambiare bersaglio da spettatore | Tocco breve fuori dalla UI, se abilitato | Click breve fuori dalla UI, se abilitato |
| Aprire le impostazioni | Usare il pulsante nell'HUD | Usare il pulsante nell'HUD |
L'input di gioco usa il primo tocco disponibile. Le interazioni sulla UI vengono escluse dalla preparazione del tiro. Un nuovo tiro è consentito solo quando la pallina ha terminato il movimento e non è già entrata in buca.
Il ciclo della partita è:
- Aprire il menu e selezionare la modalità.
- In modalità locale, scegliere il percorso; online, creare o raggiungere una stanza e lasciare la scelta al Master Client.
- Mirare, tirare e attendere l'arresto della pallina.
- In caso di uscita dal campo, attendere il riposizionamento e riprovare.
- Raggiungere la buca e visualizzare il risultato rispetto al par.
- Se restano altri partecipanti, seguirli con la camera spettatore.
- A completamento della partita, visualizzare il riepilogo quando previsto e tornare al menu.
Le impostazioni mettono in pausa il tempo di gioco nelle modalità locali. Online il tempo continua a scorrere, mentre il menu aperto blocca l'input di tiro del giocatore: una pausa locale non deve interrompere la simulazione degli altri partecipanti.
Le versioni riportate sono quelle presenti nei file del progetto, non requisiti basati sull'ultima versione disponibile.
| Tecnologia | Versione/configurazione | Utilizzo |
|---|---|---|
| Unity Editor | 6000.0.43f1 | Motore, editor, scene, fisica e build |
| C# / MonoBehaviour | Script in Assets/Scripts |
Gameplay e coordinamento dei sistemi |
| Universal Render Pipeline | 17.0.4 | Rendering con profili Mobile e PC |
| Unity uGUI e TextMesh Pro | uGUI 2.0.0 | Menu, HUD, testi e controlli |
| Photon PUN 2 | 2.55, incluso in Assets/Photon |
Connessione, stanze, caricamento delle scene e sincronizzazione |
| FMOD for Unity | 2.03.20, incluso in Assets/Plugins/FMOD |
Eventi audio, bank e mixer |
| Unity Input System | 1.13.1 | Pacchetto e asset di azioni presenti nel progetto |
| Multiplayer Play Mode | 1.3.3 | Supporto a sessioni di sviluppo con giocatori virtuali |
| Unity Test Framework | 1.4.6 | Pacchetto disponibile; non risulta una suite di test del gameplay |
Il manifest include anche AI Navigation, Post Processing, Timeline, Visual Scripting e strumenti IDE. La presenza di questi pacchetti non implica che siano usati da tutti i sistemi: per esempio, l'IA della pallina segue waypoint e non usa NavMesh.
I controller di tiro e spettatore leggono attualmente UnityEngine.Input. Le Player Settings hanno Active Input Handling = Both, così l'input tradizionale può convivere con il nuovo Input System. La sola modifica di InputSystem_Actions.inputactions non cambia quindi i controlli implementati in questi script.
Il progetto contiene una configurazione specifica per Android, insieme a un profilo PC utile allo sviluppo e alle build desktop. Le impostazioni iOS presenti non documentano una distribuzione iOS completata.
- Unity Hub e Unity 6000.0.43f1.
- Modulo Android Build Support, con SDK, NDK e OpenJDK, per produrre una build Android.
- Git LFS per recuperare correttamente bank FMOD e sorgenti audio gestiti tramite LFS.
- Un App ID Photon Realtime valido per il multiplayer.
- FMOD Studio compatibile con la versione 2.03.20 dell'integrazione se si vogliono modificare gli eventi o ricostruire le bank. Il progetto include già bank compilate.
-
Recuperare il repository e, se necessario, completare il download LFS:
git lfs install git lfs pull
-
In Unity Hub, aggiungere la cartella contenente
Assets,PackageseProjectSettings. -
Aprire il progetto con la versione Unity indicata e attendere importazione degli asset e risoluzione dei pacchetti.
-
Aprire
Assets/Maps/AdvMenuScene.unitye avviare Play Mode. -
Provare Solo o IA, oppure attendere che i servizi Photon siano pronti per usare Host/Join.
Le modalità locali non richiedono l'ingresso in una stanza. Il servizio Photon tenta comunque la connessione all'avvio, quindi un errore di rete può comparire anche mentre si lavora sulle modalità locali.
L'asset di configurazione è Assets/Photon/PhotonUnityNetworking/Resources/PhotonServerSettings.asset. Il repository contiene un App ID configurato; per usare un'applicazione Photon propria impostare il relativo AppIdRealtime nell'Inspector.
La configurazione corrente usa regione eu e AppVersion 0.1. Le istanze che devono incontrarsi devono usare la stessa applicazione Photon, regione e versione. L'AppVersion di rete è distinta dalla versione dell'applicazione Unity, impostata a 0.1.0.
Le impostazioni FMOD puntano a:
- Progetto sorgente:
FMODProject/GreenLab_bank.fspro. - Cartella delle build audio:
FMODProject/Build. - Bank Desktop presenti:
FMODProject/Build/Desktop. - Bank distribuite con Unity:
Assets/StreamingAssets.
Se il suono manca, verificare che i file LFS siano stati scaricati e che i percorsi FMOD siano risolti. Dopo una modifica al progetto audio, ricostruire le bank in FMOD Studio e aggiornarle nell'integrazione Unity.
| Scena | Stato | Ruolo |
|---|---|---|
Assets/Maps/AdvMenuScene.unity |
Abilitata, prima scena | Menu principale PAR CITY, modalità, impostazioni e selezione della mappa |
Assets/Maps/Level-1.unity |
Abilitata | Primo percorso cittadino, par 3 |
Assets/Maps/Level-2.unity |
Abilitata | Secondo percorso cittadino, par 2 |
Assets/Maps/MenuScene.unity |
Disabilitata | Menu alternativo presente nel repository |
In Assets/Scenes sono presenti SampleScene, TestingScene, LogicScene e HUDScene, escluse dall'elenco della build. Il pacchetto grafico include inoltre scene dimostrative per varie pipeline.
Entrambi i livelli usano un ambiente urbano cartoon, con strade, edifici, veicoli e vegetazione. Il primo comprende rampe, mezzi tubi, imbuto e loop; il secondo comprende rampe, mezzi tubi, tubi, imbuto e spirale. I percorsi includono waypoint dell'IA e volumi per rilevare l'uscita dal campo.
Il par è definito negli asset Level1_MapSettings.asset e Level2_MapSettings.asset, assegnati ai rispettivi LevelManager.
GreenLab/
├── Assets/
│ ├── 2D/ UI, prefab del menu/HUD, video e immagini
│ ├── 3D/ Modelli, ostacoli, pallina IA e materiali fisici
│ ├── Maps/ Menu e livelli principali, dati di illuminazione
│ ├── Scenes/ Scene ausiliarie e di prova
│ ├── Scripts/
│ │ ├── Editor/ Strumenti di configurazione e diagnosi
│ │ ├── Networking/ Servizi Photon, palline e spettatore online
│ │ └── ScriptableObjects/ Configurazioni di tiro, camera, mappe e spettatore
│ ├── Resources/Network/ Prefab GolfBall e impostazioni spettatore online
│ ├── Settings/ Pipeline URP, renderer e volume profile
│ ├── StreamingAssets/ Bank FMOD per il runtime
│ ├── Photon/ SDK Photon PUN e Realtime
│ ├── Plugins/FMOD/ Integrazione e librerie FMOD
│ ├── ithappy/Cartoon_City_Free/ Ambiente urbano e asset grafici
│ └── TextMesh Pro/ Font e risorse dei testi
├── FMODProject/ Progetto Studio, metadati, sorgenti e bank
├── Packages/ Dipendenze Unity
└── ProjectSettings/ Configurazione condivisa dell'applicazione
I file .meta mantengono i GUID usati dai riferimenti Unity e devono accompagnare gli asset. Library, Temp, obj, log, impostazioni locali e file IDE generati sono esclusi dal versionamento secondo .gitignore. I file .sln e .csproj sono prodotti da Unity.
.gitattributes usa Git LFS per .bank, .wav, .mp3, .ogg, .aiff e .flac: i contenuti audio restano versionati senza essere salvati come normali blob Git.
Il gameplay è organizzato in componenti Unity con responsabilità distinte. BallController coordina il tiro, mentre input, applicazione della forza, stato, resistenza e respawn sono implementati separatamente.
flowchart TD
Menu["MenuManager e MapSelectionManager"] --> Level["LevelManager"]
Input["BallInputController"] --> Ball["BallController"]
Input --> Preview["BallAimPreview e GameHUDManager"]
Ball --> Launcher["BallLauncher"]
Ball --> State["BallState"]
Ball --> Rolling["BallRollingResistance"]
Ball --> Respawn["BallRespawnController"]
Ball --> Counter["StrokeCounter"]
Ball --> Audio["AudioManager"]
AI["AIController e WaypointTrigger"] --> Physics["Rigidbody e collisioni"]
Launcher --> Physics
Physics --> Hole["HoleTrigger"]
Physics --> Out["OutBox"]
Out --> Respawn
Hole --> Level
Level --> Results["VictoryManager e ScoreboardManager"]
Level --> LocalSpec["LocalSpectatorController"]
Authority["NetworkBall: IBallControlAuthority"] --> Ball
Authority --> OnlineSpec["NetworkSpectatorController"]
| Area | Script | Responsabilità |
|---|---|---|
| Menu e partita | MenuManager, MapSelectionManager, InGameManager, LevelManager |
Modalità, scene, pausa, uscita, partecipanti e conclusione |
| Pallina | BallController, BallInputController, BallLauncher, BallState, BallRollingResistance, BallRespawnController |
Validazione, gesto di tiro, impulso, arresto, frenata e recupero |
| Campo e risultati | HoleTrigger, OutBox, StrokeCounter, VictoryManager, ScoreboardManager |
Buca, fuori campo, colpi, risultato e riepilogo |
| Camera e UI | BallCameraController, BallAimPreview, GameHUDManager, HoleIndicator, VideoPlaceholder |
Inquadratura, mira, HUD, indicatore e preparazione del video |
| IA | AIController, WaypointTrigger |
Decisione del tiro e avanzamento nel percorso |
| Spettatore | LocalSpectatorController, NetworkSpectatorController, SpectatorPointerInput |
Selezione del bersaglio e distinzione tra click e trascinamento |
| Audio | AudioManager, AudioMenuController |
Riproduzione degli eventi e controllo dei volumi |
| Rete | PhotonConnectionService, PhotonRoomService, NetworkMenuController, NetworkBallSpawner, NetworkBall |
Connessione, stanza, UI, spawn e replica |
| Tipo | Contenuto | Asset principali |
|---|---|---|
ShotData |
Forza massima/minima, moltiplicatore e limiti del trascinamento | Assets/Scripts/ScriptableObjects/ShotSettings.asset |
CameraData |
Distanza, parametri orbitali e velocità di rotazione | Assets/Scripts/ScriptableObjects/CameraSettings.asset |
MapData |
Nome, nome scena e par | Level1_MapSettings.asset, Level2_MapSettings.asset |
LocalSpectatorSettings |
Ritardo, visibilità del risultato e cambio manuale | Assets/Scripts/ScriptableObjects/LocalSpectatorSettings.asset |
NetworkSpectatorSettings |
Stesse opzioni per lo spettatore online | Assets/Resources/Network/NetworkSpectatorSettings.asset |
Questa separazione rende il bilanciamento modificabile dall'Inspector e permette a più componenti di condividere gli stessi parametri. Richiede però di verificare anche asset e override dei prefab quando si cambia un valore nel codice.
- Componenti separati e prefab riutilizzabili: consentono di modificare una parte del comportamento e riutilizzare UI e ostacoli tra i livelli. I collegamenti serializzati restano parte della configurazione da mantenere.
- Autorità tramite
IBallControlAuthority: il gameplay chiede se può controllare la pallina senza implementare direttamente la logica Photon. In assenza di un componente di autorità, il controllo è locale. - Servizi online persistenti: vengono creati prima della prima scena e conservati con
DontDestroyOnLoad, mantenendo connessione e stato della stanza durante i caricamenti. - Eventi per aggiornare il menu di rete: connessione, stanza, giocatori ed errori notificano la UI, separando presentazione e operazioni Photon.
- PlayerPrefs per piccoli dati locali: memorizzano volumi e identità di rete. I risultati delle partite non vengono salvati in uno storico persistente.
- Compatibilità dei riferimenti serializzati: gli attributi
FormerlySerializedAsconservano collegamenti dopo la rinomina di diversi campi. - Reset delle variabili statiche all'avvio: modalità, flag del menu e singleton vengono inizializzati con
SubsystemRegistration, evitando di riutilizzare stati della sessione precedente.
BallInputController intercetta la pallina tramite raycast. Il prefab distingue il collider fisico da un SphereCollider trigger più ampio, usato per rendere selezionabile la pallina anche su uno schermo touch. La selezione considera il collider più vicino, evitando di mirare attraverso un ostacolo.
La posizione del puntatore è proiettata su un piano orizzontale all'altezza della pallina. La direzione del tiro è il vettore che va dal puntatore verso la pallina; la sua lunghezza viene limitata da fDragLimit.
BallController.TryLaunch verifica autorità, stato e validità del tiro, salva la posizione di recupero e chiede a BallLauncher di applicare:
forzaGrezza = distanzaTrascinamento × fDragMultiplier
forzaApplicata = Clamp(Max(forzaGrezza, fMinLaunchForce), 0, fMaxForce)
impulso = direzioneNormalizzata × forzaApplicata
Il Rigidbody riceve la forza con ForceMode.Impulse. Dopo un lancio riuscito vengono riprodotto l'effetto sonoro, aggiornato lo stato, attivata la resistenza al rotolamento e incrementato il contatore dei colpi.
L'asset ShotSettings.asset contiene attualmente:
| Parametro | Valore |
|---|---|
fMaxForce |
70 |
fMinLaunchForce |
0 |
fDragMultiplier |
10 |
fDragLimit |
5 |
fMinDragToLaunch |
0 |
Con questi valori, il trascinamento del giocatore arriva a un impulso scalare massimo di 50, pur avendo un limite generale di forza pari a 70. Per cambiare la potenza realmente raggiungibile bisogna considerare insieme limite di trascinamento, moltiplicatore e forza massima.
BallState osserva velocità lineare e angolare in FixedUpdate. La pallina torna disponibile al tiro soltanto dopo essere rimasta sotto entrambe le soglie per un tempo di conferma. Questo evita che un rallentamento istantaneo durante il movimento venga interpretato come un arresto.
Nel prefab di rete condiviso con le scene, le soglie salvate sono 0,1 per la velocità lineare, 0,8 per quella angolare e 0,15 secondi di conferma. La fisica del progetto usa un passo fisso di 0,02 secondi.
BallRollingResistance applica una decelerazione orizzontale progressiva. Il tiro determina una distanza iniziale con resistenza ridotta; oltre questa distanza, la forza resistente aumenta lungo una rampa. La distanza è misurata rispetto al punto di partenza del tiro, non come somma di tutti i tratti percorsi.
La combinazione di impulso fisico, materiali e frenata controllata permette rimbalzi e attraversamento degli ostacoli mantenendo regolabile il tempo necessario a fermarsi. La linea di mira mostra direzione e intensità relativa: non simula l'intera traiettoria o le collisioni future.
Il prefab della pallina usa interpolazione del Rigidbody e collision detection ContinuousDynamic. In Assets/3D sono presenti materiali fisici distinti per pallina, terreno, pareti e superfici a basso rimbalzo.
BallRespawnController conserva posizione e rotazione iniziali e le aggiorna prima di ogni tiro. OutBox avvia un recupero dopo un ritardo configurabile e lo annulla se la pallina esce dal volume prima della scadenza.
Il recupero azzera le velocità, ripristina la posa, aggiorna il transform fisico e annulla la resistenza al rotolamento. Non aggiunge un colpo di penalità: rimane conteggiato il tiro che ha causato l'uscita.
HoleTrigger ignora i collider trigger, le palline già a segno e quelle senza colpi registrati. Per la pallina del giocatore verifica anche l'autorità, impedendo di registrare localmente l'arrivo di una replica remota.
Il risultato usa colpi - par. Un singolo colpo produce sempre Hole in One!; le altre etichette seguono la differenza dal par. A partire da quattro colpi sopra il par, il pannello mostra il numero totale di colpi. Il riepilogo elenca nome e colpi dei partecipanti nell'ordine di completamento, senza ordinamento competitivo automatico.
AIController usa la sequenza di stati:
Starting → Targeting → Shooting → Waiting → Targeting
└─ arrivo in buca → Finished
Dopo il ritardo iniziale, l'IA calcola la direzione orizzontale verso il waypoint corrente e una potenza proporzionale alla distanza, limitata tra un minimo e un massimo. Una deviazione angolare casuale rende i tiri variabili. Dopo il lancio aspetta che la velocità scenda sotto la soglia e che trascorra il ritardo tra due tiri.
WaypointTrigger sceglie casualmente uno dei waypoint successivi all'attraversamento del volume. LevelManager genera la pallina IA e le assegna uno dei bersagli iniziali della mappa. La buca interrompe la macchina a stati e registra il risultato come IA.
Nel prefab Assets/3D/Props/AI_GolfBall.prefab, la configurazione corrente è:
| Parametro | Valore |
|---|---|
| Potenza per unità di distanza | 1 |
| Potenza minima / massima | 5 / 40 |
| Errore angolare | ±1° |
| Soglia di arresto | 0,05 |
| Ritardo tra i tiri | 5 secondi |
| Ritardo iniziale | 6 secondi |
Scelta progettuale: una macchina a stati con un grafo di waypoint è semplice da leggere, regolare e visualizzare tramite Gizmos. Il percorso dipende dall'autore del livello; l'IA non calcola una traiettoria fisica ottimale e non cerca autonomamente un nuovo percorso in caso di blocco.
L'IA applica direttamente l'impulso al Rigidbody. Non passa attraverso BallController.TryLaunch e non usa ShotData: il suo bilanciamento è indipendente e non attiva automaticamente tutti gli effetti del tiro del giocatore, come la notifica della resistenza al rotolamento.
PhotonConnectionService crea automaticamente il contenitore dei servizi, abilita l'esecuzione in background e imposta:
AutomaticallySyncScene = true.SendRate = 30.SerializationRate = 20.
Un identificativo viene generato e salvato in PlayerPrefs con la chiave GreenLab.Network.UserId. Nell'Editor viene aggiunto il PID del processo, permettendo a più istanze di avere identità distinte. Il nickname predefinito usa il prefisso Player-.
PhotonRoomService crea stanze con codice di 6 caratteri, usando l'alfabeto ABCDEFGHJKLMNPQRSTUVWXYZ23456789, che esclude alcuni caratteri facilmente confondibili. In ingresso il codice viene normalizzato rimuovendo spazi e trattini e convertendolo in maiuscolo.
La stanza è aperta, ma non visibile nell'elenco pubblico. La configurazione predefinita prevede 4 giocatori, TTL del giocatore di 60.000 ms, rimozione immediata della stanza vuota e pulizia della cache all'uscita. Una collisione sul codice genera un nuovo tentativo, fino a un massimo di cinque tentativi.
Sono gestiti gli stati di connessione e di stanza, gli errori di creazione/accesso, l'elenco dei partecipanti e la notifica di cambio del Master Client. Il TTL non equivale a un flusso completo di riconnessione: quest'ultimo non è implementato negli script di gioco.
Solo il Master Client può scegliere la mappa online. MapSelectionManager usa PhotonNetwork.LoadLevel e la sincronizzazione automatica porta gli altri client nella stessa scena. I pulsanti vengono disabilitati dopo la richiesta per evitare caricamenti duplicati.
NetworkBallSpawner trova la pallina presente nella scena e ne usa la posa come riferimento. In modalità online la disattiva e crea una pallina del giocatore tramite PhotonNetwork.Instantiate("Network/GolfBall", ...), con un piccolo offset legato all'ActorNumber.
Il prefab deve essere in Assets/Resources/Network/GolfBall.prefab: è il percorso usato dal caricamento Photon corrente. Lo spawner cerca palline locali già esistenti prima di crearne una nuova e riprova quando la scena non è ancora pronta.
NetworkBall implementa IPunObservable e IBallControlAuthority. Ogni client simula fisicamente la propria pallina e invia posizione, rotazione, velocità lineare e angolare, stato di arrivo e numero dei colpi.
Sulle repliche remote:
- Il Rigidbody è cinematico.
- Input, stato locale e resistenza sono disabilitati.
- I collider della pallina sono disabilitati, evitando una seconda simulazione delle collisioni sul client osservatore.
- Posizione e rotazione vengono interpolate.
- La velocità lineare compensa il ritardo tramite extrapolazione limitata a 0,1 secondi.
- Errori di posizione superiori a 1,5 unità causano un riallineamento immediato.
- Colpi e completamento aggiornano il riepilogo locale e la modalità spettatore.
La velocità angolare viene trasmessa ma non viene attualmente applicata alla replica. Il prefab usa UnreliableOnChange e ownership fissa.
Scelta progettuale: l'autorità del proprietario mantiene reattivo il tiro locale e riduce la complessità della simulazione condivisa. Il Master Client coordina la scelta della scena, mentre non è un server autoritativo della fisica. I risultati dipendono dai dati comunicati dai client; il codice non implementa validazione server o collisioni competitive condivise tra palline.
Il prefab AdvMenu contiene il menu principale, le impostazioni, la selezione della modalità e quella dei percorsi. NetworkMenuController collega i pulsanti Host/Join, il codice e il testo di stato ai servizi di rete. L'host vede il codice e il numero di giocatori; il client rimane in attesa della scelta della mappa.
VideoPlaceholder, dichiarato nel file VideoPlaceHolder.cs, mantiene visibile un'immagine mentre VideoPlayer prepara Assets/2D/Background.mp4. Al completamento nasconde il placeholder e avvia il video.
GameHUDManager mostra il contatore dei colpi e aggiorna una barra di potenza con gradiente. La barra usa un moltiplicatore grafico predefinito pari a 7: il riempimento non rappresenta in rapporto uno a uno l'impulso fisico applicato.
BallAimPreview disegna la linea di direzione durante la mira e genera un cerchio sotto la pallina quando è disponibile al tiro. HoleIndicator proietta sullo schermo il bersaglio e ne limita la posizione ai bordi, mantenendolo visibile anche quando la buca è fuori dall'inquadratura.
BallCameraController aggiorna l'orbita in LateUpdate, guarda il bersaglio e limita l'inclinazione. CameraSettings.asset salva distanza 1, pitch minimo 10°, massimo 75° e velocità di rotazione 0,2. L'orientamento iniziale effettivo è gestito dai campi del controller.
Quando il giocatore termina:
LocalSpectatorControllersegue i partecipanti locali rimasti, ad esempio l'IA.NetworkSpectatorControllerseleziona le palline remote non ancora arrivate, ordinate per ActorNumber, e aggiorna l'elenco ogni 0,25 secondi.- La camera e il contatore HUD vengono associati al bersaglio osservato.
SpectatorPointerInputdistingue il cambio bersaglio da una rotazione della camera usando una soglia di trascinamento di 8 pixel.
Gli asset correnti prevedono 1 secondo di ritardo per lo spettatore locale e 3 secondi per quello online, nascondono il pannello del risultato al cambio e abilitano il cambio manuale. Queste opzioni sono modificabili senza intervenire sui controller.
AudioManager collega il gameplay a cinque eventi:
| Evento | Percorso FMOD | Quando viene usato |
|---|---|---|
| Tiro | event:/SFX/Shots/Shot |
Lancio riuscito del giocatore |
| Applauso | event:/SFX/Ambient/Clap_01 |
Arrivo del giocatore in buca |
| Click UI | event:/SFX/Buttons/UI_Click |
Interazioni UI collegate al metodo audio |
| Musica del menu | event:/SFX/Music/UI_Background |
Avvio del menu |
| Ambiente della mappa | event:/SFX/Ambient/Map_Background |
Avvio del livello |
Gli effetti di tiro e applauso vengono richiesti con una posizione nel mondo. Musica e ambiente usano istanze conservate dal manager, interrotte e rilasciate quando necessario o alla distruzione dell'oggetto.
Il volume Master controlla bus:/; gli altri gruppi controllano vca:/Music, vca:/Ambience e vca:/Effects. AudioMenuController normalizza i valori degli slider, applica le modifiche e salva le preferenze alla chiusura del pannello o quando l'applicazione va in pausa.
Le chiavi persistenti sono Audio.MasterVolume, Audio.MusicVolume, Audio.AmbienceVolume e Audio.EffectsVolume. In assenza di preferenze il valore è 1. Un gruppo FMOD non trovato produce un warning, senza ripeterlo continuamente finché l'errore permane.
Il progetto Studio contiene le bank Master, SFX_Ambient, SFX_Buttons, SFX_Music e SFX_Shots. In StreamingAssets è presente anche UI.bank, mentre l'elenco delle bank configurate nell'integrazione usa le quattro bank SFX e Master.
Scelta progettuale: FMOD separa la progettazione del suono dal codice di gioco; eventi e VCA permettono di modificare contenuti e mix mantenendo stabili i punti di chiamata. I nomi dei VCA e le bank compilate diventano però parte del contratto da preservare.
L'ambientazione usa gli asset Cartoon City Free di ithappy, integrati con modelli di ostacoli e palline contenuti in Assets/3D. L'interfaccia impiega immagini, sprite per pulsanti e slider, icone, TextMesh Pro e uno sfondo video.
Il progetto usa URP e due profili:
| Impostazione | Mobile | PC |
|---|---|---|
| Pipeline | Mobile_RPAsset.asset |
PC_RPAsset.asset |
| Renderer | Mobile_Renderer.asset |
PC_Renderer.asset |
| Render Scale | 0,8 | 1 |
| Risoluzione shadow map della luce principale | 1024 | 2048 |
| MSAA configurato | 1, nessun multisampling | 1, nessun multisampling |
| Piattaforme predefinite rilevanti | Android e iPhone | Standalone |
Sono presenti volume profile, dati di illuminazione e reflection probe dei livelli. La pipeline globale nelle Graphics Settings è quella PC; i Quality Settings associano il profilo Mobile alle piattaforme mobili.
Scelta progettuale: due asset di pipeline consentono di ridurre risoluzione interna e costo delle ombre su mobile mantenendo un profilo distinto per desktop. Questi parametri descrivono il compromesso configurato; non attestano prestazioni misurate o un frame rate garantito sui dispositivi.
- Aprire File → Build Profiles e selezionare Android.
- Installare il modulo Android se manca e attivare il profilo.
- Verificare nell'elenco scene che
AdvMenuScenesia la prima scena abilitata, seguita daLevel-1eLevel-2. - Verificare riferimenti Photon, prefab di rete e bank FMOD.
- Controllare le Player Settings e produrre la build con Build o Build And Run.
La configurazione salvata include:
| Impostazione | Valore |
|---|---|
| Product Name | GreenLab |
| Versione | 0.1.0 |
| Application Identifier Android | com.rezzo.greenlab |
| Version Code | 1 |
| Minimum API Level | 23 |
| Target API Level | Automatico |
| Scripting Backend Android | IL2CPP |
| Target Architecture | ARM64 |
| Orientamento | Auto Rotation, con tutti gli orientamenti abilitati |
La scelta ARM64 esclude i dispositivi esclusivamente a 32 bit dalla build corrente. Per una build desktop attivare il profilo Standalone e conservare lo stesso elenco scene. Non sono presenti una pipeline CI di build o script di distribuzione dedicati.
| Comando | Utilizzo |
|---|---|
GreenLab → Networking → Configure Advanced Menu |
Collega i riferimenti del menu online, crea il testo di stato se manca e porta la scena avanzata al primo posto nella build |
GreenLab → Networking → Configure Golf Ball Prefab |
Configura NetworkBall, PhotonView, componenti osservati, sincronizzazione, ownership e collider fisico del prefab esistente |
GreenLab → Networking → Photon Room Test |
In Play Mode, permette di creare/raggiungere/lasciare una stanza e vedere partecipanti e ruolo |
I comandi di configurazione vanno eseguiti fuori da Play Mode. Il comando della pallina richiede che il prefab esista già nel percorso previsto; non genera da zero modello e gameplay.
PhotonNetworkDebugOverlay è compilato solo nell'Editor e nelle Development Build. Espone stato, codice, elenco dei giocatori e comandi di stanza. La finestra di test e l'overlay operano sui servizi Photon: per verificare il gameplay completo usare il menu Host/Join, che imposta anche la modalità di gioco.
FmodVirtualPlayerSetup riconosce l'argomento --virtual-project-clone dei giocatori virtuali, copia il progetto FMOD dal progetto principale e aggiorna le bank. La ricerca del progetto principale presume la struttura di cartelle delle copie virtuali prevista dallo script.
- Creare una scena in
Assets/Maps, partendo da una scena esistente per conservare i collegamenti necessari. - Inserire pallina di scena, camera con
BallCameraController, EventSystem, HUD, AudioManager e GameManager. - Creare un
MapDatacon nome, scena e par, quindi assegnarlo aLevelManager. - Collegare il trigger della buca, i pannelli del risultato e del riepilogo.
- Inserire volumi
OutBoxdove serve il recupero. - Per l'IA, assegnare spawn, prefab, bersagli iniziali e collegamenti dei
WaypointTrigger. - Aggiungere e abilitare la scena in Build Profiles.
- Estendere
MapSelectionManagere il menu: l'implementazione corrente ha due pulsanti e due nomi scena espliciti. - Verificare sia il caricamento locale sia quello Photon e l'associazione di camera/HUD alla pallina generata.
Il campo sSceneName di MapData non pilota attualmente la selezione: il caricamento usa i campi di MapSelectionManager. Per una selezione dinamica occorre collegare esplicitamente menu e dati delle mappe.
- Modificare
ShotSettings.assetper potenza e limiti del gesto di tiro. - Modificare
BallState,BallRollingResistance, Rigidbody e materiali del prefab per arresto e movimento. - Modificare il prefab IA per potenza, precisione e tempi di decisione.
- Modificare
CameraSettings.assete i controller di scena per l'inquadratura. - Modificare gli asset spettatore per ritardo e cambio manuale.
- Verificare gli override nei livelli: possono sostituire i valori del prefab.
Per nuovi suoni, creare gli eventi nel progetto FMOD, assegnarli alle bank, ricostruire le bank e collegare gli EventReference al codice o ai prefab. Mantenere i VCA richiesti dal controllo volume.
Per cambiare i dati sincronizzati, modificare scrittura e lettura di NetworkBall.OnPhotonSerializeView nello stesso ordine e coordinare la versione delle istanze. Per aumentare i partecipanti, modificare il limite richiesto alla creazione della stanza e verificare spazio di spawn, interfaccia e conclusione della partita.
La descrizione deriva dall'ispezione statica dei sorgenti e degli asset. Non certifica l'esecuzione su dispositivo, la qualità del collegamento online o le prestazioni.
Il repository include scene e strumenti di prova, ma non risulta una suite automatizzata di test specifica per il gameplay. Una verifica manuale utile comprende:
| Area | Verifica |
|---|---|
| Avvio | Partenza da AdvMenuScene, video, navigazione tra i pannelli |
| Tiro | Direzione opposta al trascinamento, rilascio, un solo colpo per lancio, impossibilità di tirare durante il movimento |
| Campo | Collisioni con gli ostacoli, recupero fuori campo e annullamento del recupero quando si esce dal volume |
| Risultati | Buca dopo almeno un tiro, par corretto, riepilogo e ritorno al menu |
| IA | Bersaglio iniziale, transizioni dei waypoint, tiri, arresto in buca e spettatore |
| Audio | Eventi, quattro gruppi di volume e persistenza dopo riavvio |
| Online | Due istanze nella stessa stanza, scelta mappa dell'host, una pallina per client, replica, colpi e spettatore |
| Uscita | Conferma di ritorno al menu, ripristino del tempo e abbandono della stanza |
| Android | Gesti touch, formati dello schermo, audio, ripresa dell'applicazione e build ARM64 |
Sono presenti i seguenti limiti dell'implementazione:
- Progressione: non sono implementati campagna, sblocco dei percorsi, salvataggio dei punteggi o classifica persistente. La partita riguarda il percorso scelto.
- Riepilogo: mostra l'ordine di completamento; non ordina per numero di colpi e non assegna esplicitamente un vincitore.
- IA: dipende dai waypoint e non dispone di ricerca del percorso, previsione delle collisioni o recupero strategico da situazioni di stallo.
- Networking: non sono implementati matchmaking pubblico, ready check, reconnect/rejoin automatico o validazione autoritativa dei risultati.
- Ingresso durante la partita: la stanza non viene chiusa quando parte la mappa; un ingresso tardivo può non essere coerente con il conteggio dei partecipanti già impostato.
- Uscita di un partecipante: il numero dei partecipanti viene impostato dallo spawner, ma non viene ricalcolato da LevelManager a ogni uscita. La sequenza finale può quindi restare in attesa di un giocatore assente.
- Cambio host: il servizio rileva il cambio Master Client, ma il menu non aggiorna esplicitamente la modalità Host/Client in quella callback. Il flusso di migrazione richiede ulteriori verifiche.
- Fine partita online: il ritorno automatico usa il caricamento locale della scena del menu e conserva la stanza; non esiste un coordinatore separato di fine match e rivincita. L'uscita esplicita dal menu di partita lascia invece la stanza.
- Dati delle mappe:
Level1_MapSettings.assetsalvasSceneName: Level 1, mentre la scena effettiva èLevel-1. Il caricamento corrente funziona tramite i nomi di MapSelectionManager; il dato va allineato prima di usarlo per un menu dinamico. - Nome del menu: il valore iniziale nello script LevelManager è
Menu; i prefab e i livelli principali lo sovrascrivono conAdvMenuScene. Nuove istanze devono avere il nome scena corretto.
Il repository contiene Photon PUN/Realtime, FMOD, Cartoon City Free di ithappy e risorse TextMesh Pro. Questi contenuti mantengono le condizioni dei rispettivi fornitori.
Sono presenti la licenza OFL del font Liberation Sans e il file di attribuzione EmojiOne nelle risorse TextMesh Pro. I sorgenti audio FMOD comprendono file con nomi che riportano autori quali kellieskitchen, craigsmith, nox_sound, ienba e yukon456; il nome del file da solo non documenta le condizioni di utilizzo.
© 2026 Giuseppe Cricrì e Tommaso Rezzani. Tutti i diritti riservati sul codice e sui contenuti originali del progetto.
GreenLab / PAR CITY è pubblicato come progetto di portfolio, per mostrare le competenze di sviluppo e le scelte tecniche adottate.
Non viene concessa una licenza generale per il riutilizzo, la modifica o la ridistribuzione del codice originale. Per tali utilizzi è necessario ottenere l’autorizzazione dei rispettivi titolari, salvo quanto consentito dalla legge e dalle condizioni di GitHub.
La build APK è resa disponibile per provare il gioco a fini dimostrativi.
Asset grafici, audio, font e componenti di terze parti mantengono le rispettive licenze.