This article has been localized into Italian by the community.
In this article series, we're building a complete Snake game from scratch. It makes sense to start with the Introduction and then work your way through the articles one by one, to get the full understanding.
If you want to get the complete source code for the game at once, to get started modifying and learning from it right now, consider downloading all our samples!
Creare e muovere the Snake
Nell'ultimo articolo, abbiamo creato l'area della nostra implementazione di SnakeWPF dove per il serpente si può spostare. Ora è il momento di creare il serpente reale per poi farlo muovere nell'area. Ancora una volta, useremo la classe Rectangle di WPF per creare un serpente di una certa lunghezza, con ogni elemento che ha la stessa larghezza e altezza dei quadrati di sfondo: la costante SnakeSquareSize !
Creiamo il serpente
Disegneremo il serpente con un metodo chiamato DrawSnake() - il metodo è in realtà abbastanza semplice, ma richiede un po' di roba extra, inclusa una nuova classe chiamata SnakePart, così come alcuni campi extra sulla classe Window. Iniziamo con la classe SnakePart, che definiamo in un nuovo file (SnakePart.cs, ad esempio):
using System.Windows;
namespace WpfTutorialSamples.Games
{
public class SnakePart
{
public UIElement UiElement { get; set; }
public Point Position { get; set; }
public bool IsHead { get; set; }
}
}
Questa semplice classe conterrà informazioni su ogni parte del serpente: dov'è posizionato l'elemento nella nostra area di gioco, quale UIElement (un rettangolo, nel nostro caso) rappresenta la parte, ed è questa la parte della testa del serpente o no? Useremo tutto in un secondo momento, ma prima, all'interno della nostra classe Window, dobbiamo definire un paio di campi da usare nel nostro metodo DrawSnake () (e successivamente anche in altri metodi):
public partial class SnakeWPFSample : Window
{
const int SnakeSquareSize = 20;
private SolidColorBrush snakeBodyBrush = Brushes.Green;
private SolidColorBrush snakeHeadBrush = Brushes.YellowGreen;
private List<SnakePart> snakeParts = new List<SnakePart>();
......
Definiamo due SolidColorBrush , uno per il corpo e uno per la testa. Definiamo anche un elenco & lt; SnakePart & gt ;, che manterrà un riferimento a tutte le parti del serpente. Con ciò che abbiamo definito, possiamo ora implementare il nostro metodo DrawSnake():
private void DrawSnake()
{
foreach(SnakePart snakePart in snakeParts)
{
if(snakePart.UiElement == null)
{
snakePart.UiElement = new Rectangle()
{
Width = SnakeSquareSize,
Height = SnakeSquareSize,
Fill = (snakePart.IsHead ? snakeHeadBrush : snakeBodyBrush)
};
GameArea.Children.Add(snakePart.UiElement);
Canvas.SetTop(snakePart.UiElement, snakePart.Position.Y);
Canvas.SetLeft(snakePart.UiElement, snakePart.Position.X);
}
}
}
Come puoi vedere, questo metodo non è particolarmente complicato: passiamo in rassegna la lista snakeParts e, per ogni item, controlliamo se per questo elemento è stato specificato un UIElement - in caso contrario, lo creiamo (è un rettangolo) e lo aggiungiamo all'area di gioco, salvando un riferimento ad esso sulla proprietà UiElement dell'istanza SnakePart. Nota come utilizziamo la proprietà Position dell'istanza SnakePart per posizionare l'elemento effettivo all'interno dell'area di gioco di GameArea.
Il trucco qui è ovviamente che le parti effettive del serpente saranno definite altrove, permettendoci di aggiungere una o più parti al serpente, dare loro la posizione desiderata e quindi fare in modo che il metodo DrawSnake () faccia il lavoro reale per noi . Lo faremo come parte dello stesso processo usato per spostare il serpente.
Muovere il serpente
Per alimentare il metodo DrawSnake (), dobbiamo popolare la lista snakeParts . Questa lista serve costantemente come base per disegnare ogni elemento del serpente, quindi lo useremo anche per creare movimento per il serpente. Il processo di spostamento del serpente consiste sostanzialmente nell'aggiungere un nuovo elemento, nella direzione in cui il serpente si sta muovendo, e quindi eliminare l'ultima parte del serpente. Questo farà sembrare che stiamo effettivamente spostando ogni elemento, ma in effetti, ne stiamo aggiungendo di nuovi eliminando quelli vecchi.
Quindi, avremo bisogno di un metodo MoveSnake (), che ti mostrerò tra un minuto, ma prima dobbiamo aggiungere altro nella parte superiore della nostra definizione della classe Window:
public partial class SnakeWPFSample : Window
{
const int SnakeSquareSize = 20;
private SolidColorBrush snakeBodyBrush = Brushes.Green;
private SolidColorBrush snakeHeadBrush = Brushes.YellowGreen;
private List<SnakePart> snakeParts = new List<SnakePart>();
public enum SnakeDirection { Left, Right, Up, Down };
private SnakeDirection snakeDirection = SnakeDirection.Right;
private int snakeLength;
......
Abbiamo aggiunto una nuova enumerazione, chiamata SnakeDirection , che dovrebbe essere piuttosto autoesplicativa. Per questo, abbiamo un campo privato per contenere la direzione attuale effettiva ( snakeDirection ), e quindi abbiamo una variabile intera per contenere la lunghezza desiderata del serpente ( snakeLength ). Con questo in atto, siamo pronti per implementare il metodo MoveSnake () . È un po 'lungo, quindi ho aggiunto commenti in linea a ciascuna delle parti importanti:
private void MoveSnake()
{
// Remove the last part of the snake, in preparation of the new part added below
while(snakeParts.Count >= snakeLength)
{
GameArea.Children.Remove(snakeParts[0].UiElement);
snakeParts.RemoveAt(0);
}
// Next up, we'll add a new element to the snake, which will be the (new) head
// Therefore, we mark all existing parts as non-head (body) elements and then
// we make sure that they use the body brush
foreach(SnakePart snakePart in snakeParts)
{
(snakePart.UiElement as Rectangle).Fill = snakeBodyBrush;
snakePart.IsHead = false;
}
// Determine in which direction to expand the snake, based on the current direction
SnakePart snakeHead = snakeParts[snakeParts.Count - 1];
double nextX = snakeHead.Position.X;
double nextY = snakeHead.Position.Y;
switch(snakeDirection)
{
case SnakeDirection.Left:
nextX -= SnakeSquareSize;
break;
case SnakeDirection.Right:
nextX += SnakeSquareSize;
break;
case SnakeDirection.Up:
nextY -= SnakeSquareSize;
break;
case SnakeDirection.Down:
nextY += SnakeSquareSize;
break;
}
// Now add the new head part to our list of snake parts...
snakeParts.Add(new SnakePart()
{
Position = new Point(nextX, nextY),
IsHead = true
});
//... and then have it drawn!
DrawSnake();
// We'll get to this later...
//DoCollisionCheck();
}
Con questo in atto, ora abbiamo tutta la logica necessaria per creare movimento per il serpente. Nota come usiamo costantemente la costante SnakeSquareSize in tutti gli aspetti del gioco, dal disegno del motivo a scacchiera di sfondo alla creazione e aggiunta al serpente.
Sommario
Dal primo articolo, ora abbiamo uno sfondo e da questo articolo, abbiamo il codice per disegnare e spostare il serpente. Ma anche con questa logica in atto, non c'è ancora nessun movimento reale o persino un vero serpente nell'area di gioco, perché non abbiamo ancora chiamato nessuno di questi metodi.
L'invito all'azione per il movimento del serpente deve provenire da una fonte ripetitiva, perché il serpente dovrebbe essere costantemente in movimento finché il gioco è in esecuzione - in WPF, abbiamo la classe DispatcherTimer che ci aiuterà in questo. Il movimento continuo del serpente, usando un timer, sarà l'oggetto del prossimo articolo.