Visualizzazione post con etichetta traiettorie personaggi. Mostra tutti i post
Visualizzazione post con etichetta traiettorie personaggi. Mostra tutti i post

lunedì 4 luglio 2011

Sparo di un cannone (moto proiettile)

Una delle cose più importanti nella realizzazione di un videogioco è la concretizzazione del sistema fisico, ovvero la realizzazione di corpi che, in primissima istanza, si comportano come se fossero soggetti a forza di gravità. Avrete notato che se ponete in una scena un piano ed un cubo, ad esempio, e premete Play dopo aver posizionato opportunamente una camera, il cubo rimarra sospeso nel vuoto.

Devo necessariamente premettere un paio di cose prima di andare avanti: anzitutto la differenza tra vista Globale e Locale. La prima, in effetti, fa riferimento ad una "visione" degli assi direzionali x,y,z in modo assoluto, quindi indipendente dai vari oggetti sulla scena, mentre la seconda è relativa a dove "sta guardando" un determinato GameObject. La distinzione viene fatta perchè, nel caso in cui ruotiate un certo personaggio, per esempio, il suo sistema di assi puo' non coincidere con quello globale, e puo' essere utile in alcuni casi passare dall'una all'altra.




Come forse saprete Unity3D permette di applicare la fisica ad un qualsiasi GameObject, ed il modo più semplice per farlo è quello di selezionare l'oggetto e dal menu Components selezionare Phisics-> RigidBody. Questo vi metterà a disposizione una nuova componente nella quale potrete decidere le caratteristiche fisiche dell'oggetto, tra cui:
  • mass: la massa o "peso" dell'oggetto
  • drag: il "rallentamento" che subisce l'oggetto durante il moto, ad esempio "effetto paracadute" che potete settare a 20, per esempio;
  • angular drag: il "rallentamento" che subisce l'oggetto durante la rotazione, che potete settare a 5, per esempio;
  • collision detect: discreta, continua, o continua e dinamica;
  • use gravity: oggetto sottoposto alla forza di gravità lungo l'asse globale y, in direzione -1.


A questo punto proveremo ad applicare una forza ad esempio ad una sfera sulla quale avrete applicato la componente fisica, allo scopo di emulare il classico "moto di un proiettile" (il cannone che spara un colpo, per intenderci). Per farlo ricorreremo alla pratica funzione addForce, applicata ovviamente una sola volta quando l'oggetto è stato creato (ovvero dentro Awake(): vedi anche ciclo di vita di un GameObject):

var force = 20.0;
var direzione = Vector3 (0,1,1);

function Awake(){
    rigidbody.AddForce(direzione * force);
}

Il valore 20 rende il moto del proiettile visibile rispetto ad una massa fissata ad 1, se inserite altri valori dovrete calibrare per tentativi il resto di conseguenza. Inoltre ho introdotto un vettore direzione che spara, quindi, lungo (0,1,1) come è possibile vedere anche nel video:




Se poi ad esempio decidiamo di vincolare la direzione del movimento senza scomodare complesse formule matematiche, potrei congelare uno o più assi (freeze) e fare in modo che il movimento sia diretto verso l'alto (lungo l'asse y, tipo lancio di un missile).


mercoledì 29 giugno 2011

Nemici che si muovono verso di noi secondo varie traiettorie

Nella realizzazione di uno sparatutto in prima persona (cosiddetti FPS) una delle prime cose da fronteggiare riguarda le modalità con cui gli enemy si avvicineranno a voi. Ad esempio in un videogioco di base horror potremmo avere degli zombi dalla caratteristica camminata "zoppa" che si avvicinano a noi seguendo una determinata traiettoria, del tipo:
  • linea retta;
  • linea curva ("spirale");

Questo almeno è quello che è possibile definire in prima istanza; ora, associando allo zombie uno script di update fatto come segue, regolando i parametri di ingresso delle "velocità" rispetto ad x e a z è possibile di fatto ottenere entrambe le traiettorie.

//walk each to other
var other: GameObject;
var vx = 0.3;
var vz = 0.3;

function walk(){
 animation.CrossFade("zombiewalkcycle");
 
 var p1 = transform.position;
 var p2 = other.transform.position;

 //var tmp = Vector3(0,0,0);

 transform.LookAt(p2); //definisce la direzione
  
 //0.92 vuol dire che si avvicina al punto di molto
 //ad es. 0.50 si avvicina di meno e si stabilizza
 //girandogli attorno
 var xx = (p2.x - p2.x*0.92) * vx;
 var yy = 0;
 var zz = (p2.z - p2.z*0.92) * vz;
 
 transform.Translate(xx, yy, zz, Space.Self);
   
 var d = Vector3.Distance (p1, p2); 
 print ("Distanza tra "+p1+" e "+ p2 +" = "+ d);

}

function Update() {
walk();
}

L'effetto è rappresentato nel seguente video:



Qualche nota di commento sul codice: quello che vi basta cambiare è il valore di vx e vy, che io ho impostato in tre modalità diverse ragionando sugli ordini di grandezza (ricordo che un odg rappresenta, parlando "a spanne", una "decina", per cui da 10 a 1000 vi sono due odg). Per cui ho impostato:

Update nemico per ogni frame:

 var xx = (p.x - p.x*0.92) * vx;
 var zz = (p.z - p.z*0.92) * vz;

dove ho testato tre possibilità:
1) vx=vy=0.05 spirale 1
2) vx=0.5, vy=0.05 spirale 2
3) vx=0.05, vy=0.5 linea "quasi" retta

Ovviamente il tutto è completamente indipendente, da quanto ho sperimentato, dalla posizione relativa dei due oggetti: il funzionamento si basa sul fatto che l'oggetto che si muove (lo zombie, per capirci) calcola il proprio forward "guardando" nella direzione del target (il vostro player, ovvero quello che nel video resta fermo). L'algoritmo, regolando opportunamente la velocità, converge anche se il player-target si muove nel tempo, e gli inizierà presto a girare intorno, mantenendo uan distanza costante che dipende dallo 0.92 che ho lasciato nel codice:

//0.92 vuol dire che si avvicina al punto di molto
 //ad es. 0.50 si avvicina di meno e si stabilizza
 //girandogli attorno
 var xx = (p2.x - p2.x*0.92) * vx;
 var yy = 0;
 var zz = (p2.z - p2.z*0.92) * vz;

Qualsiasi osservazione a riguardo nei commenti: spero che la cosa possa essere utile per qualcuno di voi.