Hello Pong: φτιάχνοντας ένα παιχνίδι σε 10 λεπτά, μέρος 1ο
Δημοσιεύθηκε από Κώστας Αναγνώστου στο Ιουλίου 3, 2009
Συνειδητοποίησα το εξής: σκοπός του blog αυτού είναι να δείξει μερικά πράγματα σχετικά με την ανάπτυξη βιντεοπαιχνιδιών (και μερικές αρχές τριδιάστατων γραφικών) και όχι να διδάξει προγραμματισμό ή κάποια γλώσσα προγραμματισμού. Για το λόγο αυτό, θα αναστρέψω την μέχρι τώρα προσέγγιση μου, εστιάζοντας στη δημιουργία παιχνιδιών αντί της εκμάθησης C#. Θεωρώ ότι οι αναγνώστες του blog έχουν κάποια γνώση σχετικά με το προγραμματισμό γενικότερα, αρκετή για να ξεκινήσουμε την ανάπτυξη βιντεοπαιχνιδιών. Ό,τι εξειδικευμένη γνώση χρειάζεται πάνω στην C# θα την εξηγώ στην πορεία.
Οπότε σήμερα θα πάρουμε μια βαθιά ανάσα και θα κάνουμε μια βουτιά στα βαθιά φτιάχνοντας ένα πλήρες παιχνίδι σε XNA Game Studio. Στο κλασσικό προγραμματισμό, συνηθίζεται το πρώτο πρόγραμμα που αναπτύσσει κάποιος σε μια γλώσσα προγραμματισμού να τυπώνει την φράση “Hello World!” στην οθόνη. Στο κόσμο της ανάπτυξης βιντεοπαιχνιδιών (ιδιαίτερα με το XNA Game Studio), το αντίστοιχο πρώτο παιχνίδι είναι το Pong (που είναι και το παιχνίδι που ξεκίνησε την βιομηχανία βιντεοπαιχνιδιών).
Καταρχάς πρέπει, αν δεν το έχουμε κάνει ήδη, να εγκαταστήσουμε το XNA Game Studio. Από την σελίδα Creator’s Club κατεβάζουμε και εγκαθιστούμε πρώτα το Visual C# Express 2008. Οι οδηγίες προτείνουν να τρέξουμε και στην συνέχεια να κλείσουμε μια φορά το περιβάλλον της Visual C# πριν συνεχίσουμε την εγκατάσταση. Στην συνέχεια κατεβάζουμε και εγκαθιστούμε και το XNA Game Studio 3.1. Αν έχουμε ήδη τη προηγούμενη έκδοση του XNA Game Studio (3.0) θα πρέπει να την απεγκαταστήσουμε πριν την εγκατάσταση της νέας έκδοση. Τέλος τρέχουμε τη Visual C# και το είμαστε έτοιμοι να αρχίσουμε την ανάπτυξη.
Στο περιβάλλον την Visual C# επιλέγουμε από το μενού File/New Project.

Στο παράθυρο που ανοίγει επιλέγουμε ως τύπο project το XNA Game Studio 3.1, και το πρότυπο (template) Windows Game (3.1). Δίπλα σε αυτό μπορείτε να δείτε και πρότυπα για παιχνίδια ειδικά για Xbox360 και Zune. Αναφέρω απλά ότι ένα παιχνίδι που στοχεύει τα Windows δεν μπορεί να τρέξει σε Xbox360 (και το αντίστροφο) ακόμα και αν ο κώδικας είναι ο ίδιος. Αυτός είναι ο λόγος που υπάρχουν τα ειδικά templates για κάθε πλατφόρμα. Τέλος δίνουμε το όνομα Pong στο project και πατάμε το OK.
Η Visual C# έχει δημιουργήσει τώρα ένα σκελετό προγράμματος παιχνιδιού. Ο κύριος κώδικας του «παιχνιδιού» βρίσκεται στο αρχείο Game1.cs. Το παιχνίδι είναι έτοιμο να τρέξει και θα το κάνει αν πατήσουμε το F5, εμφανίζοντας μια μπλε οθόνη:

Για κάποιο λόγο το πρότυπο παιχνιδιού σε Windows θεωρεί ότι έχουμε συνδεδεμένο κάποιο χειριστήριο (controller) στον υπολογιστή και δεν υποστηρίζει έξοδο της εφαρμογής με κάποιο πλήκτρο (από το πληκτρολόγιο, πχ το Escape). Θα το διορθώσουμε αργότερα αυτό, προς το παρόν κλείστε το παράθυρο με το [x] πάνω-δεξιά.
Επιστρέφοντας στο Game1.cs και κοιτώντας το κώδικα που αυτό περιέχει κάνουμε αρχικά την παρατήρηση ότι όλος ο κώδικας του παιχνιδιού περιλαμβάνεται σε μια class με το όνομα Game1. To παιχνίδι που τρέχουμε είναι συνεπώς ένα αντικείμενο (object) τύπου Game1. Από το class Game1 μας ενδιαφέρουν αρχικά 3 μέθοδοι, οι LoadContent(), Update() και Draw().
Η LoadContent εκτελείται μια φορά κατά την έναρξη του παιχνιδιού. Χρησιμοποιούμε την μέθοδο αυτή για να φορτώσουμε από το δίσκο όλο το περιεχόμενο του παιχνιδιού όπως εικόνες (υφές), τριδιάστατα μοντέλα, ήχους. Βλέπουμε ότι εξ’ ορισμού η LoadContent δημιουργεί ένα αντικείμενο τύπου SpriteBatch. Αυτό θα το χρησιμοποιήσουμε αργότερα για να απεικονίσουμε εικόνες στην οθόνη.
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
}
Η δεύτερη σημαντική μέθοδος είναι η Update. Η μέθοδος αυτή καλείται 60 φορές το δευτερόλεπτο (εξ’ ορισμού) και κάνει όλες τις απαραίτητες ενημερώσεις στην λογική του παιχνιδιού, στην θέση και την κατάσταση των χαρακτήρων του, επεξεργάζεται την είσοδο από το πληκτρολόγιο ή το controller κλπ.
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
base.Update(gameTime);
}
Όταν θέλουμε να τραβήξουμε μια φωτογραφία στο πραγματικό κόσμο, μετακινούμε τα έπιπλα, τοποθετούμε τα άτομα στη σκηνή, τους λέμε να φτιάξουν τα μαλλιά τους, να χαμογελάσουν και να κάτσουν ακίνητα. Κατ’ αναλογία, στην μέθοδο Update τοποθετούμε κώδικα που στήνει το σκηνικό και όλα τα αντικείμενα της σκηνής και τα ετοιμάζει για φωτογράφηση. Αυτό είναι κάτι που κάνει η επόμενη μέθοδος, η Draw.
Στην Draw συμπεριλαμβάνουμε όλο το κώδικα που απεικονίζει τον κόσμο του παιχνιδιού στην οθόνη. Η Draw καλείται επίσης, αμέσως μετά την Update, 60 φορές το δευτερόλεπτο εξ’ορισμού (αυτό μπορούμε να το αλλάξουμε).
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
base.Draw(gameTime);
}
Μέχρι στιγμής η Draw δεν κάνει τίποτα άλλο από το να καθαρίζει το παράθυρο και θα θέτει το χρώμα του φόντου σε μπλε (Color.CornflowerBlue). Μπορείτε να αλλάξετε το χρώμα αυτό σε όποιο επιθυμείτε φυσικά (Color.Black, Color.Red κλπ).
Η αλληλουχία ενημέρωσης της κατάσταση του κόσμου του παιχνιδιού (Update) και της απεικόνισης του (Draw) ονομάζεται game loop (βρόχος παιχνιδιού), και υπάρχει σε όλα τα παιχνίδια, από το πιο απλό (Pong) μέχρι το πιο σύνθετο (Grand Theft Auto IV). Το game loop ιδανικά εκτελείται 60 φορές το δευτερόλεπτο για ρυθμό απεικόνισης (framerate) 60 Hz. Αν το παιχνίδι έχει πολλά και σύνθετα αντικείμενα με πολύπλοκη φυσική εξομοίωση και τεχνητή νοημοσύνη, ο ρυθμός απεικόνισης μπορεί να είναι (αρκετά) χαμηλότερος.
Πριν κλείσω την περιγραφή του σκελετού παιχνιδιού που δημιουργεί το XNA Game Studio πρέπει να κάνω μια αναφορά στα αντικείμενα GamePad και GraphicsDevice που βλέπουμε στο κώδικα. To XNA γενικώς χρησιμοποιεί αντικείμενα για να εκπροσωπήσει, και να δώσει πρόσβαση σε λειτουργίες τους, διάφορους πόρους της παιχνιδομηχανής. Το GamePad είναι ένα αντικείμενο που αντιπροσωπεύει το χειριστήριο που τυχόν έχουμε συνδέσει στη παιχνιδομηχανή (χρησιμοποιώ τον όρο με την γενική έννοια, συμπεριλαμβανομένου και τον υπολογιστή). Στο συγκεκριμένο παράδειγμα το χρησιμοποιούμε για να ρωτήσουμε την τρέχουσα κατάσταση του και να μάθουμε αν ο χρήστης έχει πατήσει κάποιο συγκεκριμένο κουμπί (το Back για την ακρίβεια). Για το πληκτρολόγιο το αντίστοιχο αντικείμενο είναι το Keyboard. Αντίστοιχα, το GraphicsDevice είναι ένα αντικείμενο που εκπροσωπεί το παράθυρο μέσα στο οποίο απεικονίζουμε το παιχνίδι. Η μέθοδος του Clear που καλούμε στο Draw καθαρίζει το παράθυρο και θέτει ως φόντο ένα συγκεκριμένο χρώμα.
Με γνώση του σκελετού του παιχνιδιού και των βασικών μεθόδων του, μπορούμε να προχωρήσουμε στην δημιουργία του παιχνιδιού Pong, κάτι που θα κάνουμε στο επόμενο άρθρο. Ως συνήθως σχόλια και απορίες είναι καλοδεχούμενα.

Dimitris Gkanatsios είπε
Great! Keep posting!!!
Σπύρος (spahar) είπε
Πρώτα απ’ όλα να πω ότι συμφωνώ με τη νέα προσέγγιση. Σίγουρα οι τελείως αρχάριοι θα δυσκολευτούν περισσότερο, αλλά ακόμα και για αυτούς θα είναι πιο ευχάριστο έτσι.
Περιμένω το 2ο άρθρο για τη δημιουργία του Pong.
darklynx είπε
Πολύ ορθή η νέα προσέγγιση.Όπως στη σειρά άρθρων του OpenGL υποτέθηκε ότι υπήρχαν οι απαραίτητες βάσεις έτσι και εδώ.Δυναμική αρχή με το Pong και αναμένουμε την συνέχεια!
konsnos είπε
Πολύ καλή και κατατοπιστική παρουσίαση. Με ενδιαφέρει πολύ η συνέχεια.
Γιώργος είπε
Όντως είναι καλύτερα κατευθείαν στο ΧΝΑ.
Περιμένουμε την συνέχεια.