Benvenuto Visitatore(Log In|Registrati)

> Unity tips, il topic dove si postano risoluzioni a problemi quotidiani
Lief
messaggio17 Apr 2018, 12:04
Messaggio #1





Gruppo: Gamer
Messaggi: 498
Iscritto il: 29 November 16
Utente Nr.: 21.351
BGE Deus Ex 1
Playing Fallout New Vegas
SO Altro




Come forse qualcuno di voi saprà da un po' di tempo mi occupo dello sviluppo di giochi in Unity a livello personale (ma a livello professionale sono comunque Web Developer back e front end) e mi capita spesso di scontrarmi con problematiche di vario genere che possono far perdere giornate intere ma che non sempre hanno soluzioni realmente complesse.

Questo topic vuole semplicemente essere un elenco di suggerimenti aperto a tutti.
Io in particolare posterò suggerimenti relativi a Unity di tanto in tanto quando penserò che vale la pena farlo.

Elenco tips:
- Remap Input Personalizzato
- Virtual Joystick
- FPS Constructor by Leonardo Boselli
- Serializzazione, Traduzione, Salvataggio Dati
- Drag&Drop
- Muovere un Oggetto con un Click
- Hack & Slash RPG da zero BurgZergArcade
- Mischiare una lista di oggetti non iterabili
- Collider e Trigger, tutti i segreti
- Tocchiamo il terreno? Raycast camera, come sparare raggi in tutte le direzioni
- Serializzazione Multi-livello. Oggetti complessi custom.
- Oauth 2.0 e Save Cloud - Parte 1: La teoria
- Oauth 2.0 e Save Cloud - Parte 2: Google Drive - Autenticazione, Upload, Download

Come primo tips voglio iniziare con il problema che mi sono ritrovato ad affrontare più recentemente, problema che in un prossimo futuro non sarà più rilevante: il Remap dell'input in Unity a runtime.

Avete presente il menù per cambiare i comandi da usare nel gioco (esempio per cambiare il comando per saltare da Spazio ad un qualsiasi altro bottone)?
È una classica opzione che viene data ai giocatori PC (mentre è spesso assente su console) ed è anche una delle più grandi limitazioni del sistema di input attuale di Unity. Unity infatti permette il remap dell'Input solo prima di avviare il gioco in una schermata completamente separata dal gioco tipica dei videogiochi Unity.

Come risolvere il problema?
- Usare il nuovo Input System, che però è ancora in una fase di sviluppo molto embrionale, richiede una versione di Unity diversa da quella di default, è soggetto a bug e cambiamenti. In futuro ovviamente questa sarà la migliore opzione disponibile e il tutto sarà decisamente più semplice.
- Comprare sull'assets store o scaricare su git uno dei tanti sistemi di Input alternativi. Sono sistemi che però richiedono l'import di molto codice che spesso non ci interessa e che va mantenuto ad ogni aggiornamento di Unity, il che può essere giustificato ma è comunque qualcosa che è meglio evitare se si è in grado di farlo.
- Creare il nostro personalissimo sistema di Input.

Se avete deciso di usare il terzo sistema vi posso dare una mano.
Prima di tutto bisogna dire che rimappare semplicemente i tasti è decisamente semplice, vi basterà creare una mappa chiave, valore impostando la chiave a stringaNomeInput (esempio "Salto"), il valore al KeyCode a cui lo volete associare (esempio KeyCode.Space)... A quel punto fate due metodi un getter e un setter e, durante lo Start, settate tutti i valori.
Per ricavare l'input vi basterà sostituire nel codice i vostri vecchi:
if(Input.GetButtonDown("Salto"))

con

if(Input.GetKeyDown(VostraClasseInput.GetInput("Salto")))

se prima Input dipendeva da un sistema settato nell'InputManager di Unity, ora dipenderà dal vostro sistema che restituirà un KeyCode nella mappa a seconda della chiave passata e il tutto verrà letto con l'Input.GetKey GetKeyDown GetKeyUp del sistema di default di Unity.

Nel vostro menù di remap vi basterà richiamare il
VostraClasseInput.SetInput("Salto", key) (dove key è il KeyCode rilevato)
per cambiare il valore nella mappa e far rispondere il GetKeyDown ad un altro pulsante.

Principale problema:
Gli Axis non hanno KeyCode quindi come fare a rilevare il float relativo al movimento?

Per quanto riguarda la tastiera è sufficiente aggirare il problema:

float avanti = 0f;

if(Input.GetKey(VostraClasseInput.GetInput("Avanti")))
avanti = 1f;
else if(Input.GetKey(VostraClasseInput.GetInput("Indietro")))
avanti = -1f;
else
avanti = 0f;

Leggendo il float avanti possiamo sapere se l'utente sta premendo il pulsante per andare avanti, quello per andare indietro oppure non si sta muovendo.

Ovviamente il codice si può abbellire, ad esempio si può aggiungere un sistema per incrementare la variabile "avanti" da 0 ad 1 non in maniera istantanea:
avanti = avanti < 1f ? avanti + Mathf.Lerp(0, 1, Time.deltaTime * 5) : 1f;

il codice sopra significa: finché la variabile "avanti" è minore di 1 incrementa avanti da 0 a 1 in base al tempo moltiplicato per 5 (il che significa che potete variare questo tempo per rendere il tutto più lento o più veloce), in caso contrario (quando la variabile raggiunge 1) vogliamo che non venga più incrementata.

Tutto questo però è ok su tastiera, se volessimo usare un gamepad inizierebbero i problemi. I joystick infatti hanno un KeyCode variabile rilevabile solo quando si preme il joystick, non quando si stanno usando gli axis.
Inoltre, il sistema che abbiamo usato prima non ha senso per un joystick (visto che un joystick può rilevare valori intermedi tra 0 e 1 che possono venir utilizzati).

Cosa fare quindi?

L'idea è simile a quella usata in precedenza: usare il vecchio sistema di Input a nostro vantaggio. Questa volta l'idea è creare nel vecchio sistema di Input tutti gli Axis che siamo certi di voler utilizzare (esempio VerticalWindows, VerticalMac, VerticalLinux, VerticalIOS, VerticalAndroid) e settare quali assi dovranno leggere, di base tutti i joystick leggono un asse verticale e uno orizzontale (X e Y) per il joystick di sinistra, mentre la cosa varia per il joystick di destra e il d-pad a seconda della piattaforma (esempio su windows il joystick di destra è sugli assi 4 e 5, mentre su mac sono gli assi 3 e 4).

Nella nostra classe di input bisognerà aggiungere una mappa chiave valore stringa-stringa, la chiave sarà come richiameremo la lettura dell'asse nella get, mentre il valore sarà il reale nome del joystick settato nel vecchio InputManager.
Esempio

Nell'Input Manager abbiamo dato il nome "Vertical" e "Horizontal" agli assi X e Y, "RightVertical" e "RightHorizontal" agli assi 4 e 5.

Nella nostra mappa dovremo mappare questi assi in un modo simile a questo:
VostraClasseInput.SetJoypad("JoyVertical", "Vertical");
VostraClasseInput.SetJoypad("JoyHorizontal", "Horizontal");
VostraClasseInput.SetJoypad("JoyVertical2", "RightVertical");
VostraClasseInput.SetJoypad("JoyHorizontal2", "RightHorizontal");

Ovviamente se volessimo rimappare il tutto in modo da invertire ciò che vogliamo fare con joystick sinistro e destro, nel nostro sistema di remap, al passo di rilevare il joystick utilizzato (cosa che richiederebbe l'associazione di un keycode che varia da piattaforma a piattaforma e non sempre è disponibile (ad esempio non lo è sul dpad)) ci conviene settare il tutto dando semplicemente all'utente la possibilità di cambiare joystick di movimento /camera (esempi) con joystick sinistra, destra o dpad in un sistema di selezione.
VostraClasseInput.SetJoypad("JoyVertical", "RightVertical");
VostraClasseInput.SetJoypad("JoyHorizontal", "RightHorizontal");
VostraClasseInput.SetJoypad("JoyVertical2", "Vertical");
VostraClasseInput.SetJoypad("JoyHorizontal2", "Horizontal");


Per richiamare il tutto invece ci basta il sistema usato per i bottini con la sola differenza che ci serve Input.GetAxis al posto di GetKey.

Ovviamente sarebbe molto più semplice se unity fosse in grado di fare setaxis e setbutton e magari anche di rilevare l'axis usato ma sfortunatamente al momento non pare sia possibile (lo sarà con il prossimo sistema di input), ovviamente se avete idee per migliorare il mio tip scrivetele pure e spero che anche altri vorranno condividere i propri.

Al prossimo tip

Messaggio modificato da Lief il 3 Aug 2018, 10:52
 
 
Start new topic
Risposte
Lief
messaggio1 Aug 2019, 15:41
Messaggio #2





Gruppo: Gamer
Messaggi: 498
Iscritto il: 29 November 16
Utente Nr.: 21.351
BGE Deus Ex 1
Playing Fallout New Vegas
SO Altro




Ultimo capitolo dedicato ai salvataggi su cloud: Mega.

Per implementare mega è necessario utilizzare il plugin apposito creato per c#
https://gpailler.github.io/MegaApiClient/
A quell'indirizzo trovate praticamente tutto.

Per l'installazione vi consiglio di scaricare semplicemente MegaApiClient.dll versione net46 e metterla dentro una cartella Plugins/lib su Unity. Potrete così importarla nel vostro codice senza problemi.
Ricordatevi inoltre di utilizzare solo una versione di JsonNet-Lite. Quindi se avete importato UnityGoogleDrive sostituite la versione inclusa in quel pacchetto (meno aggiornata) con quella di MegaApiClient (che è comunque compatibile anche con UnityGoogleDrive).
AGGIORNAMENTO 10-08-2019
Quello che scrivo qua sopra per l'installazione è valido solo se si usa come Scripting Backend Mono senza stripping del codice:
https://docs.unity3d.com/Manual/ManagedCodeStripping.html

IL2CPP (il futuro di Unity e al momento l'unico utilizzabile per iOS) necessità però di uno stripping minimo a Low (non si può disabilitare).
Inoltre IL2CPP essendo compilato in anticipo (AOT) non è compatibile con la classe Emit utilizzata da JsonNet di MegaApiClient che utilizza compilazione JIT (just in time ovvero al primo avvio).
Per risolvere questi problemi ho passato 2 notti in bianco e contattato l'autore di MegaApiClient e quello di JsonNet for Unity... ma alla fin fine ho trovato una soluzione funzionante:
1. Scaricate Json .Net for Unity da qui:
https://assetstore.unity.com/packages/tools...for-unity-11347
vi serve solo lo zip con i sorgenti. Apriteli in Visual Studio, aggiungete un riferimento a UnityEngine.dll (dovete fare sfoglia e scegliere la cartella d'installazione di Unity \Editor\Data\Managed).
Cambiate la versione da 8.3 a 10.0.0.0.
Compilate come Release.
Copiate il nuovo dll e xml ottenuto.
Né MegaApiClient, né UnityGoogleDrive dovrebbero lamentarsi.
Questa nuova versione non usando JIT è compatibile con la compilazione AOT, quindi può essere utilizzata con IL2CPP (che appunto trasforma il codice in C++ e quindi compila in codice macchina e non è quindi compatibile con compilazione al primo avvio come l'interprete mono).
2. Create un file link.xml (basta che sia nella cartella Assets o in una sottocartella):
<linker>
<assembly fullname="MegaApiClient"> // the name of the assembly
<type fullname="CG.Web.MegaApiClient.*" preserve="all"/> // excludes all namespaces and classes recursively under MyNamespace
</assembly>
</linker>
Questo file dice a IL2CPP di Unity di non fare stripping del codice della libreria MegaApiClient nel namespace CG.Web.MegaApiClient e in tutti i figli in maniera ricorsiva. Lo stripping del codice elimina infatti alcuni getter utilizzati da json .net per la serializzazione/deserializzazione il che manda a quel paese il funzionamento di MegaApiClient.
Notare che ora, se selezioniamo .NET Standard 2.0 in Unity e usassimo MegaApiClient .Net 2 il codice funziona.
3. Per rendere compatibile il tutto con .Net 4.x al momento è necessario scaricare il codice sorgente di MegaApiClient:
https://github.com/gpailler/MegaApiClient
Aprire il progetto (potete eliminare quello di test) in visual studio

Compilate per release e copiate il nuovo dll e xml (non è più necessario fare modifiche al codice perché la mia modifica è stata accettata dall'autore di megaapiclient. inoltre è probabile che alla prossima release non dovrete neanche più compilare il codice).

Spiegato così sembra semplice ma debuggare il tutto è stato un vero e proprio inferno (anche perché non funzionava solo su android quindi ho dovuto usare adb che però non legge i debug ma solo i messaggi lanciati dalle eccezioni). Oltretutto su codice non mio. Ma gli autori dei plugin gratuiti e open source hanno fatto di tutto per aiutarmi. Se avete problemi a fare questa procedura quindi contattatemi pure.
FINE AGGIORNAMENTO 10-08-2019

Ok tempo di codificare.
importate le librerie

using CG.Web.MegaApiClient;
using static CG.Web.MegaApiClient.MegaApiClient;

fate il login
var client = new MegaApiClient();
//richiede email e password
AuthInfos auth = GenerateAuthInfos(potresteprenderequestocampodauncampoInputfield.GetComponent<InputField>().text, stessacosaperlapasswordmagariconl'opzionepasswordattivainmodochevenganomostratisoloasterischi.GetComponent<InputField>().text);
//serializziamo la risposta in json
/*
[SerializeField] string email;
[SerializeField] string hash;
[SerializeField] byte[] passwordAesKey;*/
MegaResponse authorization = new MegaResponse();
authorization.Email = auth.Email;
authorization.Hash = auth.Hash;
authorization.PasswordAesKey = auth.PasswordAesKey;

//richiamiamo un metodo per aspettare la risposta
LoginToMega(client, auth);


Il nostro metodo sarà asincrono (in modo che durante il login l'utente non veda il gioco bloccato)
private async void LoginToMega(MegaApiClient client, AuthInfos auth) {
try {
//prova e attende il login asincrono
await client.LoginAsync(auth);
} catch (ApiException e) {
}
//poi fa il log out
await client.LogoutAsync();
}

In pratica se abbiamo avuto successo le credenziali inserite sono corrette.
Il file json che abbiamo salvato si utilizzerà nello stesso modo per il check del login iniziale (se le nostre credenziali sono ancora valide oppure la sessione è scaduta si vedrà qui).


Per l'upload (sempre asincrono)
private async void UploadSavesMega() {
var client = new MegaApiClient();
//prendiamo i dati salvati e dopo aver fatto il log in
MegaApiClient.AuthInfos authInfos = new MegaApiClient.AuthInfos(response.Email, response.Hash, response.PasswordAesKey);
await client.LoginAsync(authInfos);
//prendiamo tutti i nodi (ossia tutte le cartelle
IEnumerable<INode> nodes = await client.GetNodesAsync();
bool cartellaTrovata = false;
//controlliamo tutti i nodi (ossia tutte le cartelle
foreach (var child in nodes)
//e se troviamo una cartella con nome uguale a quello della nostra cartella che vogliamo riempire di salvataggi
if (child != null && child.Name != null && child.Name.Equals("NOMECARTELLASUCLOUD")) {
cartellaTrovata = true;
//riempiamo la cartella
UploadSaveFilesMega(client, child);
return;
}
//se no creiamo la cartella prima di riempirla
if (!cartellaTrovata) {
INode root = nodes.Single(x => x.Type == NodeType.Root);
INode myFolder = await client.CreateFolderAsync("NOMECARTELLASUCLOUD", root);
UploadSaveFilesMega(client, myFolder);
}

//questo è il metodo asincrono che riempe effettivamente la cartella (già presente o creata)
private async void UploadSaveFilesMega(MegaApiClient client, INode folder) {
List<string> salvataggiFile = dirInf.GetFiles().OrderByDescending(f => f.LastWriteTime).Where(f => f.Extension == ".json").Select(f => f.Name).ToList();

foreach (var salvataggio in salvataggiFile) {
//prima cosa cicliamo sui salvataggi in locale e controlliamo se sono su cloud
IEnumerable<INode> nodes = await client.GetNodesAsync();
foreach (var child in nodes)
//se sono su cloud cancelliamo i vecchi salvataggi
if (child != null && child.Name != null && child.Name.Equals(salvataggio))
await client.DeleteAsync(child, false);

string path = Path.GetFullPath("pathlocalealsalvataggio" + salvataggio);
IProgress<double> progress = new Progress<double>();
//upload del file asincrono sulla cartella che abbiamo deciso del file al path locale che abbiamo deciso
INode uploadedFile = await client.UploadFileAsync(@path, folder, progress);
//possiamo anche rinominare il file dopo averlo uploadato (possiamo anche farlo prima ma poi dovremmo cambiare quello locale)
await client.RenameAsync(uploadedFile, salvataggio.Substring(0, salvataggio.Length - 5) + "-to-mega.json");
}
await client.LogoutAsync();
}

Nota bene, a differenza di dropbox e one drive e google drive, mega non ha il replace quindi dobbiamo prima cancellare i file già presenti, poi possiamo mettere quelli nuovi.
Nota bene 2, altra differenza sta nel fatto che mega accetta come parametro un path al file di cui fare l'upload, non un'array di byte, quindi il nome sarà quello originario, per questo dobbiamo fare (se vogliamo) il rename dopo (o prima).

Il download è un po' più semplice in questo caso:
var client = new MegaApiClient();
//login
MegaApiClient.AuthInfos authInfos = new MegaApiClient.AuthInfos(response.Email, response.Hash, response.PasswordAesKey);
await client.LoginAsync(authInfos);
//prendo tutti i nodi
IEnumerable<INode> nodes = await client.GetNodesAsync();

foreach (var child in nodes)
//nella cartella dei salvataggi
if (child != null && child.Name != null && child.Name.Equals("NOMECARTELLASUCLOUD")) {
DownloadSaveFilesMega(client, child);
return;
}

private async void DownloadSaveFilesMega(MegaApiClient client, INode folder) {
IEnumerable<INode> nodes = await client.GetNodesAsync();
foreach (var child in nodes)
//prendo tutti i file che finiscono in json in cui l'id del parent (ossia della cartella che abbiamo passato come parametro) è uguale alla cartella che stiamo analizzando
//è come guardare il nome, solo più preciso perché l'id delle cartelle create su mega è unico
if (child != null && child.Name != null && child.Name.EndsWith(".json") && child.ParentId == folder.Id) {
//creiamo un link per il download
Uri fileLink = await client.GetDownloadLinkAsync(child);
IProgress<double> progress = new Progress<double>();
//faccio il download e lo salvo in una cartella locale con il nome che mi pare
await client.DownloadFileAsync(fileLink, cartellalocale + nuovoNome, progress);
}
}


Con questo abbiamo concluso la sezione sui salvataggi su Cloud. Mi auguro che sia utile a qualcuno.
Il prossimo argomento devo ancora deciderlo ma visto che mi occuperò di quest e inventario oppure combat system potrebbe essere qualcosa di collegato.
Nell'attesa auguro a tutti buon coding e spero qualcuno abbia idee da condividere.

Messaggio modificato da Lief il 12 Aug 2019, 16:16
 

Inserisci in questo messaggio
- Lief   Unity tips   17 Apr 2018, 12:04
- - Lief   Oggi vi lascio con un video (non mio) relativo ai ...   23 Apr 2018, 08:19
- - utdefault   Grazie per le tips. Per chi non conosce l'engi...   23 Apr 2018, 22:29
|- - Lief   @Gwenelan grazie per aver corretto. CITAZIONE (u...   24 Apr 2018, 16:53
|- - Leonardo Boselli   CITAZIONE (Lief @ 24 Apr 2018, 17:53) Edi...   24 Apr 2018, 18:01
- - Gwenelan   Corretto io l'embedded ! Va messo solo il ...   24 Apr 2018, 01:59
|- - Leonardo Boselli   Bello questo thread! Con l'input ho smanet...   24 Apr 2018, 13:24
- - Lief   Si, l'assets store di Unity è uno dei suoi pun...   24 Apr 2018, 19:00
- - utdefault   Ok, ora tutto più chiaro. Grazie, Lief. Casomai,...   25 Apr 2018, 11:36
|- - Lief   CITAZIONE (utdefault @ 25 Apr 2018, 12:36...   25 Apr 2018, 13:58
|- - utdefault   CITAZIONE (Lief @ 25 Apr 2018, 14:58) CIT...   29 Apr 2018, 23:11
|- - Lief   CITAZIONE (utdefault @ 30 Apr 2018, 00:11...   29 Apr 2018, 23:50
- - Lief   In questi giorni sarò impegnato nel creare un picc...   28 Apr 2018, 19:47
- - Lief   Salve a tutti. Oggi vi porto un tip davvero carino...   29 Apr 2018, 12:01
- - Lief   Per il tip di oggi (visto che non ho fatto nulla d...   30 Apr 2018, 09:59
- - Lief   Il tip di oggi non è necessariamente legato a Unit...   4 May 2018, 09:12
- - Lief   Un modo molto semplice per rilevare le collisioni ...   13 May 2018, 17:09
- - Lief   Ho corretto un piccolo "bug" nel tip del...   13 May 2018, 18:55
- - Lief   Ci eravamo lasciati con una domanda: Come controll...   22 May 2018, 19:27
- - Lief   Prendendo spunto da una risposta che ho dato su st...   6 Jul 2018, 13:07
- - Lief   Quante volte avete pensato: - Ma quanto sono comod...   12 Jul 2018, 15:11
- - Lief   Piccola aggiunta al mio post precedente. 1. Ascolt...   18 Jul 2018, 14:16
- - Lief   Google Drive. Per google drive esiste fortunatame...   3 Aug 2018, 10:51
- - Lief   PS. Non posso più modificare il primo messaggio, p...   3 Apr 2019, 11:38
- - pinellos   Ciao! Stiamo mettendo su una avventura grafica...   11 Apr 2019, 17:35
- - Lief   Ciao, non ho molta esperienza con i particellari q...   16 Apr 2019, 23:50
- - Lief   Piccolo aggiornamento: Ho ripreso finalmente a lav...   28 Jul 2019, 06:59
- - Lief   Avevo detto di aver finito per oggi ma ho fatto un...   28 Jul 2019, 13:46
- - Lief   Ora finalmente vediamo cosa nasconde dietro le qui...   28 Jul 2019, 15:01
- - Lief   Piccolo tip: Non è possibile assegnare a runtime i...   29 Jul 2019, 19:08
- - Lief   E ora diamo un'occhiata alle API di One Drive....   1 Aug 2019, 15:01
- - Lief   Ultimo capitolo dedicato ai salvataggi su cloud: M...   1 Aug 2019, 15:41


Reply to this topicStart new topic
1 utenti stanno leggendo questa discussione (1 visitatori e 0 utenti anonimi)
0 utenti:

 

Modalità di visualizzazione: Passa a: Normale · Passa a: Lineare · Outline


Versione Lo-Fi Oggi è il: 19th October 2019 - 18:17