Windows Phone 7 e XNA 4: costruire giochi con agenti autonomi e Steering Behaviors

di Davide Guida, in Windows Phone 7 - XNA 4.0,

XNA è un framework, supportato anche da Windows Phone 7, che offre funzionalità ad hoc per costruire giochi, anche per piattaforme tradizionali, come XBox 360 e Windows.

In questo articolo mostreremo come costruire un semplice modello di Intelligenza Artificiale, che si basa sul concetto degli "agenti autonomi", ovvero un sistema capace di avvertire l'ambiente circostante ed agire in base ad alcune regole prestabilite, offrendo all'osservatore l'illusione di intelligenza.

L'abilità di navigare attraverso l'ambiente è data dagli steering behaviors, ovvero un set di metodi che definiscono materialmente le operazioni a disposizione dell'agente per interagire con tutto ciò che lo circonda. Gli steering behaviors possono anche essere combinati, ottenendo così dei comportamenti più complessi ed elaborati, ovviamente al costo di un maggiore tuning e più cicli di CPU.

Prima di iniziare...

Per procedere oltre, dobbiamo prima introdurre alcuni elementi di base. Il progetto a cui ci appoggeremo è di tipo "Windows Phone Game con XNA 4.0", ma la teoria generale è applicabile senza nessun problema e con pochissimi cambiamenti anche ad altri tipi di progetti: ad esempio, potremmo sfruttarli in un gioco per Windows o Xbox 360, sempre usando il framework XNA, ma anche in Silverlight.

Per prima cosa creiamo una classe che ci consenta di rappresentare il nostro "agente", definendone tutte le proprietà necessarie:

public class Entity {
  public void Update(GameTime gameTime) {...}
  public void Draw(SpriteBatch sb) {...}
  ...
}

L'implementazione è omessa per semplicità, ma analizzeremo il metodo Update più avanti.

Questo codice rappresenta il modello usato per il rendering (per i dettagli completi, date uno sguardo al codice allegato):

public Sprite Sprite = null;

Queste, invece, sono le variabili di base per gestire la posizione nello spazio e l'orientamento del nostro agente:

public Vector2 Position = Vector2.Zero;
public Vector2 Scale = Vector2.One;
public float Rotation = 0f;

La seguente variabile definisce l'ampiezza del Field of View, cioè quanto il nostro agente vede lontano. Nel nostro caso è il raggio della circonferenza centrata su Position:

public float FoV = 100f;

Queste, invece, sono le variabili che ci consentiranno di muoverci ed orientarci nello spazio e definire le proprietà fisiche dell'agente.

public float MaxSpeed = 1500f;
public float Drag = 0.2f;
public float Mass = 1f;
public Vector2 Velocity = Vector2.Zero;
public Vector2 Direction = Vector2.Zero;

Messo in piedi il tutto, passiamo ad analizzare gli Steering Behaviors.

Steering Behaviors: questi sconosciuti

Analizziamo adesso alcuni semplici behaviors: più in avanti vedremo come combinarli tra loro ed incapsularli in una classe, ad uso e consumo di Entity.

Il metodo Seek è veramente molto semplice: come prima cosa, calcoliamo la velocità necessaria per raggiungere il punto destination, inteso come la direzione dalla posizione corrente rispetto alla destinazione, scalata per la velocità massima a disposizione. L'oggetto Owner è un'istanza diEntity.

private Vector2 Seek(Vector2 destination)
{
  Vector2 vel = Vector2.Normalize(destination - Owner.Position) * Owner.MaxSpeed;
  return vel - Owner.Velocity;
}

Il metodo Flee è esattamente l'opposto di Seek: in questo caso, infatti, produciamo una forza che ci fa allontanare da un determinato punto.

private Vector2 Flee(Vector2 from)
{
  if (Vector2.Distance(Owner.Position, from) < Owner.FoV)
  {
    Vector2 vel = Vector2.Normalize(Owner.Position - from) * Owner.MaxSpeed;
    return vel - Owner.Velocity;
  }
  return Vector2.Zero;
}

Potremmo anche modificarlo per far si che venga generato un valore solo quando si entra in un determinato range dal punto from, come fatto di seguito:

private Vector2 Flee(Vector2 from)
{
  if (Vector2.Distance(Owner.Position, from) < Owner.FoV)
  {
    Vector2 vel = Vector2.Normalize(Owner.Position - from) * Owner.MaxSpeed;
    return vel - Owner.Velocity;
  }
  return Vector2.Zero;
}

Il behavior Pursuit è molto comodo quando è necessario intercettare un bersaglio mobile. Ovviamente, potremmo semplicemente usare Seek passando continuamente la posizione del target, ma il risultato non sarebbe molto "intelligente". Per questo, quello che facciamo è provare ad intercettare il bersaglio nella posizione dove crediamo si possa trovare in un particolare istante di tempo.
Il successo del metodo è dato da quanto bene si riesce a predire la posizione da raggiungere: questo aspetto potrebbe diventare particolarmente complesso, quindi è necessario raggiungere un compromesso, per evitare di sprecare troppi cicli di CPU.

3 pagine in totale: 1 2 3

Attenzione: Questo articolo contiene un allegato.

Contenuti dell'articolo

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti