Unity 3D Animazione di un esagono

Stato
Discussione chiusa ad ulteriori risposte.

LoneWolf96

Utente Bronze
7 Dicembre 2016
21
2
0
26
Premetto che non so praticamente niente di programmazione c# quindi magari a qualcuno potrà sembrare semplice ma io è dei giorni che ci provo ma non ci riesco: voglio creare uno script che, al tocco del mouse su di un esagono 2d questo ruoti di 60 gradi, però non con un semplice scatto ma con un'animazione. Ho provato questo script:
using UnityEngine;
using System.Collections;

public class animazioneesagono : MonoBehaviour {

public Animator anim;
// Use this for initialization
void Start () {
anim = GetComponent<Animator> ();
}

// Update is called once per frame
void OnMouseDown () {
if(transform.rotation.eulerAngles.z == 0){
anim.Play ("ruota");
}
else if(transform.rotation.eulerAngles.z == -60){
anim.Play ("ruota2");
}
}
}
Al primo click parte la prima animazione "ruota" che appunto lo fa ruotare di 60 gradi ma al secondo click non parte la seconda quindi non so come continuare
 
Non sapendo come siano le tue animazioni ti posso proprorre una soluzione alternativa che non ne fa uso:
C#:
        float closeEnouth = 1f;
        float speed = 10f; // velocita di animazione
        float cRot;
        Transform myTransform; // il caching migliora le performance perche evita getcomponent inutili
         const float RotationAngle = 60f; // modifica questo valore se vuoi piu o meno rotazione

        // Use this for initialization
        void Start()
        {
            myTransform = transform; //e' equale a myTransform = Getcomponent<Transform>();
            cRot = myTransform.rotation.z;
        }


        private void OnMouseDown()
        {
            SmoothRotation(RotationAngle); // ruota di sesanta gradi in senso antiorario, usa numeri negativi per il senso inverso

        }

        //chiama questa funzione per ruotare di deg gradi l'oggetto
        void SmoothRotation(float deg)
        {
            cRot = (cRot >= 360) ? cRot - 360 : cRot; // rimuovi 360 gradi se il valore e' piu grande
            cRot += deg;
            var newRot = Quaternion.Euler(0, 0, cRot); // presuppone che il gioco sia 2d perche' ruota x e y a 0
            StopAllCoroutines(); // ferma un eventuale animazione in corso avviata con la funzione sottostante
            StartCoroutine(rTo(newRot));
        }
     
        //animazione rotazione
        IEnumerator rTo(Quaternion target)
        {
           //modifica la rotazione per ogni frame
            while (Quaternion.Angle(myTransform.rotation, target) > closeEnouth)
            {
                myTransform.rotation = Quaternion.Lerp(myTransform.rotation, target, speed * Time.deltaTime);
                yield return null; // attendi frame successivo
            }
            myTransform.rotation = target;
        }

Se non capisci qualcosa chiedi pure.

PS: il codice e' testato e funziona in Unity 5
 
Ultima modifica:
Molto più complicato di quanto pensassi :asd: ma funziona alla perfezione grazie mille!!
 
Sempre relativo a questo script voglio impostare un bool in modo che si attivi quando la rotazione è a 0, ho usato questa funzione;
void Update () {
if (myTransform.rotation.z != 0){
rotazionicorrette = false;
}
else {
rotazionicorrette = true;
impostando il bool rotazionicorrette come public per vedere se funziona, però, quando il gioco parte la rotazione è a 0 e il bool è attivo, ma quando ruoto fino a tornare a 0 il bool non si riattiva, se invece cancello manualmente la rotazione dell'asse z torna true. Sarà forse collegato al fatto che le rotazioni non sono perfettamente di 60 gradi ma sforano di qualche millesimo, nonostante alla fine torni a 0?
 
Ciao, il tuo metodo e' un po espansivo perche' fai un controllo ogni frame che puo' benissimo essere evitato:

C#:
        //chiama questa funzione per ruotare di deg gradi l'oggetto
        void SmoothRotation(float deg)
        {
            cRot = (cRot >= 360) ? cRot - 360 : cRot;
            cRot += deg;

            //qui e' un buon posto dove fare il controllo, tuttavia se l'animazione e' molto lenta rischi di avere il valore impostato su true prima del tempo
            if (cRot == 0 || cRot == 360) Debug.Log("0 gradi");

            StopAllCoroutines();
            var newRot = Quaternion.Euler(0, 0, cRot); // presuppone che il gioco sia 2d
            StartCoroutine(rTo(newRot, cRot == 0 || cRot == 360 )); // il secondo parametro praticamente e' vero se la rotazione e' 0 o 360,
            // il controllo lo faccio su cRot perche e' molto piu' precisa che usare la rotazione recuperata da unity
        }

        IEnumerator rTo(Quaternion target, bool isCorrect = false)
        {
            while (Quaternion.Angle(myTransform.rotation, target) > closeEnouth)
            {
                myTransform.rotation = Quaternion.Lerp(myTransform.rotation, target, speed * Time.deltaTime);
                yield return null;
            }
            myTransform.rotation = target;

            if (isCorrect)
            {
                if (correct) Debug.Log("Il posto perfetto!");
                // qui arrivi solo al termine dell animazione!
               // scegli quello che preferisci e rimuovi quello che non ti serve
               // se scegli il primo usa la funzione rTo del post precedente, questa aggiungerebbe roba inutile
            }
        }

Ho modificato due delle funzioni per avere il massimo della precisione nel risultato ma sopratutto meno calcoli e controlli da eseguire ogni frame perche vengono eseguiti solo quando serve.
Se non ti e' chiaro qualcosa fammi sapere.

Ps: facendo i dovuti controlli ho notato che quando la rotazione e' 0,0,0 usando transform.rotation.eulerangles.z (cioe' ottengo il valore dela rotazione su z in gradi) mi viene un numero tipo 1,....e-5
 
ah ok adesso me lo builda ma quando lo provo non stampa niente in console
Strano, il codice che ti ho postato sopra l ho testato io stesso in unity. Hai fatto modificato anche la funzione smoothrotation come nel messaggio? Se lo hai fatto prova a mettere un debug. Log in rTo che ti stampi il valore di isCorrect appena chiami la coroutine così vedi che cosa ti passa alla funzione.
 
C#:
using UnityEngine;
using System.Collections;

public class rotazioneesagono : MonoBehaviour
{
    float closeEnouth = 1f;
    float speed = 10f; // velocita di animazione
    float cRot;
    Transform myTransform; // il caching migliora le performance perche evita getcomponent inutili
    const float RotationAngle = -60f; // modifica questo valore se vuoi piu o meno rotazione

    // Use this for initialization
    void Start()
    {
        myTransform = transform; //e' equale a myTransform = Getcomponent<Transform>();
        cRot = myTransform.rotation.z;
    }


    private void OnMouseDown()
    {
        SmoothRotation(RotationAngle); // ruota di sessanta gradi in senso antiorario, usa numeri negativi per il senso inverso

    }

    //chiama questa funzione per ruotare di deg gradi l'oggetto
    void SmoothRotation(float deg)
    {
        cRot = (cRot >= 360) ? cRot - 360 : cRot; // rimuovi 360 gradi se il valore e' piu grande
        cRot += deg;

        if (cRot == 0 || cRot == 360) Debug.Log("0 gradi");

        StopAllCoroutines(); // ferma un eventuale animazione in corso avviata con la funzione sottostante
        var newRot = Quaternion.Euler(0, 0, cRot); // presuppone che il gioco sia 2d perche' ruota x e y a 0
        StartCoroutine(rTo(newRot, cRot == 0 || cRot == 360));
    }

    //animazione rotazione
    IEnumerator rTo(Quaternion target, bool isCorrect = false)
    {
        //modifica la rotazione per ogni frame
        while (Quaternion.Angle(myTransform.rotation, target) > closeEnouth)
        {
            myTransform.rotation = Quaternion.Lerp(myTransform.rotation, target, speed * Time.deltaTime);
            yield return null; // attendi frame successivo
        }
        myTransform.rotation = target;
        Debug.Log (isCorrect);

        if (isCorrect)
        {
            if (true) Debug.Log("il posto giusto");
        }
    }
}
allora questo è tutto lo script ho cambiato la rotazione in negativo perchè la volevo in senso orario per il resto dovrebbe essere tutto uguale il debug.log mi da sempre false, ah una cosa, il bool isCorrect se metto if (correct) mi dice che correct non esiste in questo contesto ho fatto bene a mettere true?
 
Ultima modifica:
C#:
using UnityEngine;
using System.Collections;

public class rotazioneesagono : MonoBehaviour
{
    float closeEnouth = 1f;
    float speed = 10f; // velocita di animazione
    float cRot;
    Transform myTransform; // il caching migliora le performance perche evita getcomponent inutili
    const float RotationAngle = -60f; // modifica questo valore se vuoi piu o meno rotazione

    // Use this for initialization
    void Start()
    {
        myTransform = transform; //e' equale a myTransform = Getcomponent<Transform>();
        cRot = myTransform.rotation.z;
    }


    private void OnMouseDown()
    {
        SmoothRotation(RotationAngle); // ruota di sessanta gradi in senso antiorario, usa numeri negativi per il senso inverso

    }

    //chiama questa funzione per ruotare di deg gradi l'oggetto
    void SmoothRotation(float deg)
    {
        cRot = (cRot >= 360) ? cRot - 360 : cRot; // rimuovi 360 gradi se il valore e' piu grande
        cRot += deg;

        if (cRot == 0 || cRot == 360) Debug.Log("0 gradi");

        StopAllCoroutines(); // ferma un eventuale animazione in corso avviata con la funzione sottostante
        var newRot = Quaternion.Euler(0, 0, cRot); // presuppone che il gioco sia 2d perche' ruota x e y a 0
        StartCoroutine(rTo(newRot, cRot == 0 || cRot == 360));
    }

    //animazione rotazione
    IEnumerator rTo(Quaternion target, bool isCorrect = false)
    {
        //modifica la rotazione per ogni frame
        while (Quaternion.Angle(myTransform.rotation, target) > closeEnouth)
        {
            myTransform.rotation = Quaternion.Lerp(myTransform.rotation, target, speed * Time.deltaTime);
            yield return null; // attendi frame successivo
        }
        myTransform.rotation = target;
        Debug.Log (isCorrect);

        if (isCorrect)
        {
            if (true) Debug.Log("il posto giusto");
        }
    }
}
allora questo è tutto lo script ho cambiato la rotazione in negativo perchè la volevo in senso orario per il resto dovrebbe essere tutto uguale il debug.log mi da sempre false, ah una cosa, il bool isCorrect se metto if (correct) mi dice che correct non esiste in questo contesto ho fatto bene a mettere true?
Forse ho capito cosa non torna, e possibile che il mio codice dia qualche problemino con gli angoli negativi, il problema plausibile è che il controllo per ricavare il valore di isCorrect che da sempre false se gli si passa un angolo negativo di tipo -360 gradi.
Prova a mettere un debug. Log in smoothrotation che stampa il valore di cRot prima che venga avviata la coroutine così vedi subito che angolo usa.
Poi quando posso usare il pc faccio una prova e vedo. Per quanto riguarda il if(correct) ti da errore perché correct e una variabile non dichiarata.


Edit:
Se usa angoli negativi prova a scrivere una roba del genere prima di cRot +=deg;
C#:
cRot = (cRot <= -360) ? cRot +360 : cRot;
 
Un'ultima cosa, ho notato che se nella funzione start applico una rotazione che sia diversa da 0, appena avvio il gioco l'esagono è ruotato di tot gradi ma nel momento in cui clicco su di esso questo ruota fino ad arrivare a -60, come se quella rotazione impartita non la contasse e ripartisse da 0 gradi. Come faccio a risolverlo?
 
Un'ultima cosa, ho notato che se nella funzione start applico una rotazione che sia diversa da 0, appena avvio il gioco l'esagono è ruotato di tot gradi ma nel momento in cui clicco su di esso questo ruota fino ad arrivare a -60, come se quella rotazione impartita non la contasse e ripartisse da 0 gradi. Come faccio a risolverlo?
Mi era scappato un errorino nella funzione Start():
C#:
        void Start()
        {
            myTransform = transform;
            cRot = myTransform.rotation.eulerAngles.z;
        }
Mi ero dimenticato l'euler angles per pecuperare i gradi di rotaizione su z, prima usava la z del Quatrenion che non indica direttamente i gradi.
 
ok però il discorso è sempre lo stesso anche se parte con rotazione per esempio 120 appena clicco torna a -60 come se da qualche parte fosse impostato che la rotazione debba partire sempre da 0 solo che io non so dove mettere le mani :boh:
 
ok però il discorso è sempre lo stesso anche se parte con rotazione per esempio 120 appena clicco torna a -60 come se da qualche parte fosse impostato che la rotazione debba partire sempre da 0 solo che io non so dove mettere le mani :boh:
Non dovrebbe succedere, visto che la funzione start che ti ho mandato recupera la rotazione e la aggiunge a cRot, a cui poi si vanno ad aggiungere le rotazioni +\- 60
 
Ok ho risolto praticamente devo modificare sia la rotazione tipo transform.Rotate(Vector3.back * 60) sia il cRot perchè modificando solo la rotazione al partire dello script l'esagono appare ruotato di 60 gradi ma cliccando torna a ruotare da 0 invece modificando solo il cRot l'esagono non appare ruotato ma cliccando parte da -60, modificando tutti e due appare ruotato di 60 ed effettivamente ruota al click partendo sempre da -60
 
Ancora una cosa, prima avevo chiesto del bool, ora, questo bool mi serve per chiudere il livello nel momento in cui una moltitudine di questi si attiva contemporaneamente, il problema è che non so come fare a collegare più bool da più script
 
Ancora una cosa, prima avevo chiesto del bool, ora, questo bool mi serve per chiudere il livello nel momento in cui una moltitudine di questi si attiva contemporaneamente, il problema è che non so come fare a collegare più bool da più script
Puoi usare uno scirpt centrale del tipo:
C#:
public class MasterScript : Monobehaviour
{
   public static MasterScript instance; // dichiara il singleton

   int ExagonsCompleted = 0;
   int ExagonsToEndLevel = 10;

   void Start()
   {
     instance = this; // assegna l'istanza corrente al singleton
   }

   public void onExagonComplete()
   {
     ExagonsCompleted++;
    if(ExagonsCompleted >= ExagonsToEndLevel)
    {
       // hai completato x esagoni ed hai raggiunto il tuo obbiettivo
       EndGame();
     }
   }

void EndGame() {}

}

Per richiamare le funzioni poi (in una scena dove e' presente UN SOLO MasterScript, che puo benissimo avere il nome che vuoi)
C#:
MasterScript.instance.onExagonComplete(); // uso del singleton

Breve nota, il singleton e' una tecnica che prevede di creare un istanza di una classe e assegnarla a una variabile dello stesso tipo dichiarata all'interno della stessa classe (Vedi variabile: instance. E funzione Start).
Questo metodo e' particolarmente efficente per tenere traccia di singoli script di cui hai una sola istanza e che vengono usati spesso perche' hai un unica variabile statica in cui poter inserire la sua "referenza". Pessima abitudine e' fare una lista/array di variabili statiche.
Ti ricordo che le variabili dischiarate come static permangono fino al termine del programma quindi in qualsiasi scena puoi sempre usare la linea di codice scritta sopra, tuttavia se nella scena non c'e' un istanza della classe ti ritroverai con una Null Reference Exception.

Se qualcosa non ti e' chiaro fammi sapere
 
Avevo in mente un'idea simile ma se l'esagono esce dalla rotazione corretta nello script non viene registrato, in pratica se portassi dieci volte lo stesso esagono a rotazione 0 il livello verrebbe erroneamente concluso. Come faccio a togliere un punto se da true passa a false e non togliere niente se invece resta false?
 
Avevo in mente un'idea simile ma se l'esagono esce dalla rotazione corretta nello script non viene registrato, in pratica se portassi dieci volte lo stesso esagono a rotazione 0 il livello verrebbe erroneamente concluso. Come faccio a togliere un punto se da true passa a false e non togliere niente se invece resta false?
Per usare una funzione opposta a quella che usi per incrementare

C#:
   public void onExagonBreak()
   {
     ExagonsCompleted--;
   }
Questa funzione la chiami quando l'esagono che era completo torna ad essere non completo.
Nota: devi fare in modo che ogni singolo esagono sappia se e' completo o meno cosi da evitare errori come chiamare 2 volte questa funzione.
 
Ok ho fatto una cosa del genere
C#:
if (cRot == 0 || cRot == -360)
            finelivello.instance.onExagonComplete ();
       
        if (cRot == -60)
            finelivello.instance.onExagonBreak ();
l'ho inserito in SmoothRotation perché in IEnumerator se ruotavo velocemente prima che si concludesse la rotazione non assegnava i punti, nonostante questo però i punti non li assegna sempre, nel senso che la maggior parte delle volte appena la rotazione arriva a 0 aumenta un punto, ma a volte capita che uno o due esagoni, non appena arrivano a rotazione 0 non assegna il punto non capisco perchè, poi però se faccio ruotare di nuovo lo stesso fino a 0 lo assegna (anche se non sempre, a volte devo ripeterlo più di una volta). Ho impostato che gli esagoni partino sempre da una rotazione diversa da 0 in modo da non togliere punti erroneamente.
 
Stato
Discussione chiusa ad ulteriori risposte.