Per dare un po' di brio al nostro gioco, faremo in modo di far arrivare le fragole in modo casuale, facendole transitare dal lato destro al lato sinistro dello schermo, a diverse altezze. Se il personaggio entra in collisione con una fragola, se la mangia facendola sparire dal video, se invece se la lascia scappare e la fragola finisce fuori dallo schermo, il giocatore perde e quindi il gioco finisce. Inoltre per rendere il tutto più eccitante, oltre alle fragole aggiungeremo anche delle bombe in giro per lo schermo e se il personaggio entra in collisione con una di esse, salta per aria e quindi anche in quel caso il gioco finisce.
Per gestire tutto ciò abbiamo bisogno di un timer che scandisca il tempo che trascorre, in modo tale da poter far uscire gli oggetti ogni N secondi, e per farlo in modo casuale ci serve appunto un generatore di numeri casuali. Anzi, in realtà ce ne servono 3 in quanto ne utilizzeremo uno per le fragole, uno per le bombe e uno per determinare a che altezza questi oggetti devono uscire.
Ritorniamo alla MainPage.xaml
e inseriamo un testo per visualizzare il tempo trascorso.
<Grid Backgound="{StaticResource ApplicationPageBackgoundThemeBrush}">
<Viewbox x:Name="mainViewbox" Width="1366" Height="768" />
<TextBlock x:Name="timeText" Width="Auto" Height="Auto" Text="Tempo"
Foreground="Red" FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="10,10" />
</Grid>
Poi nel code behind gestiamo il timer del tempo, aggiungendo un oggetto timer e una variabile per conteggiare il tempo trascorso
public sealed partial class MainPage : Page
{
GameModel game;
// Aggiunge il timer
DispatcherTimer timeTimer;
int elapsedTime = 0;
public MainPage()
{
this.InitializaComponent();
}
Lo inizializziamo nella OnNavigatedTo
:
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
...
// fa partire il timer di gioco
this.timeText.Text = "0:00";
this.timeTimer = new DispatcherTimer();
this.timeTimer.Interval = TimeSpan.FromSeconds(1);
this.timeTimer.Tick += TimeTick;
this.timeTimer.Start();
}
Ed infine impostiamo la funzione che aggiorna il testo a video ogni secondo:
void TimeTick(object timer, object e)
{
int minutes;
int seconds;
// ogni secondo aggiorna il tempo a video
elapsedTime += 1;
if(elapsedTime < 60)
{
minutes = 0;
seconds = elapsedTime;
}
else
{
minutes = (int)(elapsedTime / 60);
seconds = elapsedTime % 60;
}
this.timeText.Text = minutes.ToString("#0");
this.timeText.Text += ":";
this.timeText.Text += seconds.ToString("00");
}
Se provate ad eseguire il progetto adesso, dovreste vedere il tempo che trascorre nell'angolo in alto a sinistra dello schermo.
Ora che abbiamo il tempo, possiamo aggiungere un metodo alla classe GameModel
per "creare" un nuovo oggetto di tipo Sprite
(in questo caso una fragola) e aggiungerlo al "contenitore" degli oggetti a video. Torniamo dunque al GameModel
e aggiungiamo innanzitutto la dichiarazione del generatore di numeri casuali da utilizzare per determinare le altezze di uscita:
class GameModel
{
...
private Random HeightRandom = new Random();
Creiamo poi il metodo AddObject
(gli diamo un nome generico poiché lo utilizzeremo sia per le fragole adesso che per le bombe in seguito) che si occupa di generare una nuova fragola e aggiungerla alla lista degli oggetti a video ad una altezza casuale:
public async void AddObject()
{
// genera un numero casuale per determinare l'altezza di uscita
// dell'oggetto che va da 0 (bordo superiore dello schermo)
// a 668 (= 768 - l'altezza degli oggetti che è 100)
int top = HeightRandom.Next(0, 668);
// crea un nuovo oggetto Sprite appena fuori dallo schermo a destra
Sprite newSprite = new Sprite(81, 100, new Point(1366, top));
// aggiunge l'immagine
await newSprite.SetSpriteSheet(new Uri("ms-appx:///Assets/Images/strawberry.tif"), 1, 1);
// imposta il tipo
newSprite.SpriteType = "strawberry";
// abilita le collisioni
newSprite.isCollisionEnabled = true;
// inserisce l'oggetto nella lista degli oggetti a video
this.GameBackground.AddSprite(newSprite);
}
Adesso che abbiamo il metodo lo possiamo richiamare nella MainPage per creare le fragole in modo casuale.
Dichiariamo una variabile per gestire il tempo tra una fragola e l'altra, ed un generatore di numeri casuali per avere ogni volta un intervallo di tempo diverso:
public sealed partial class MainPage : Page
{
...
int strawberryInterval = 0;
Random strawberryRandom = new Random();
Poi nella funzione che viene richiamata ad ogni tick del timer (ovvero ogni secondo) verifichiamo se è il momento di uscita di una fragola e se lo è, reimpostiamo l'intervallo per quella successiva:
void TimeTick(object timer, object e)
{
...
// verifica se è il momento di far apparire una fragola
if(strawberryInterval == 0)
{
// se è il momento la aggiunge
this.game.AddObject();
// e imposta il successivo intervallo casualmente
// tra 0 e 4 secondi
strawberryRandom = strawberryRandom.Next(0, 4);
}
else
{
// se non è il momento decrementa semplicemente l'intervallo
strawberryInterval -= 1;
}
}
Ora che le fragole vengono create non ci resta che andare nel GameLoop
e aggiungere il codice per farle avanzare sullo schermo ad ogni frame, altrimenti se ne resterebbero al loro posto fuori dall'area visibile del gioco...
private void GameLoop(Object sender, Object e)
{
...
// fa avanzare tutti gli oggetti presenti a video di 5 pixel alla volta
foreach(Sprite sprite in this.GameBackground.Sprites)
{
if(sprite.SpriteType != "player") { sprite.Move(new Point(sprite.Position.X - 5, sprite.Position.Y)); }
}
// fa ripartire il timer dello StoryBoard
this.GameloopStoryboard.Begin)();
}
Provate adesso a compilare ed eseguire il progetto e vedrete delle succulente fragole rosse passare sullo schermo, pronte per essere mangiate dal nostro affamato personaggio, che purtroppo però .... ancora non ci riesce!
Vediamo allora come possiamo aiutare il nostro amico grazie alla gestione delle collisioni.