Το προφίλ μας στο Google Plus
0

Python Game Programming (Part 7.5)

Με την παρούσα δημοσίευση ολοκληρώνουμε το Pygame Invaders και τη σειρά άρθρων προγραμματισμού παιχνιδιών στην Python, η οποία ξεκίνησε από το τεύχος 005 του περιοδικού deltaHacker. Μη φανταστείτε όμως ότι η δική σας περιπέτεια με το Pygame Invaders τελειώνει. Κάθε άλλο, θα λέγαμε, μόλις τώρα αρχίζει! Γιατί, αν μέχρι στιγμής έχετε αρκεστεί στο να διαβάζετε τον κώδικα (και να παίζετε το παιχνίδι), τώρα είναι η ώρα να αναλάβετε δράση. Όχι με το laser αλλά με το καλό σας, προγραμματιστικό πληκτρολόγιο!

Βλέπετε, το καλό με οποιοδήποτε πρόγραμμα, είτε είναι παιχνίδι είτε Σοβαρή Εφαρμογή (ΤΜ), είναι ότι η εξέλιξη του, η προσθήκη νέων δυνατοτήτων και φυσικά η βελτίωση του κώδικα και η απαλοιφή των bugs, δεν τελειώνουν ποτέ. Πάρτε για παράδειγμα το MS Office. Ξεκίνησε ως σουίτα εφαρμογών γραφείου και, με τις τελευταίες αλλάγες στη διεπαφή χρήστη, άρχισε να μοιάζει με το Space Invaders :)

Δεν σας λέμε βέβαια να μετατρέψετε το Pygame Invaders σε επεξεργαστή κειμένου, αλλά σίγουρα θα έχετε μερικές ιδέες για βελτίωση. Κι αν όχι, θα φροντίσουμε να σας δώσουμε εμείς μερικές.

Οπτική ένδειξη laser hit
Τι καλά που θα ήταν αν κάθε φορά που μας χτυπούσαν οι χαζοί εξωγήινοι, να είχαμε και μια οπτική ένδειξη! Τη δεδομένη στιγμή, η μόνη ένδειξη που έχουμε είναι ένας ήχος — και φυσικά το να βλέπουμε την ασπίδα μας να πέφτει. Σε πολλά shoot-em-up θα παρατηρήσετε ότι το διαστημοπλοιάκι αναβοσβήνει ή αλλάζει χρώμα όταν δέχεται μια βολή. Το δικό μας όμως, τίποτα. Τι πρέπει να κάνουμε; Με λίγη σκέψη καταλήγουμε στο συμπέρασμα ότι χρειαζόμαστε μια δεύτερη εικόνα για το διαστημόπλοιο μας, με ένα χρώμα ελαφρώς διαφορετικό.

Αν δείτε προσεκτικά τον κατάλογο με τα αρχεία του παιχνιδιού, θα διαπιστώσετε ότι αυτή η εικόνα είναι ήδη εκεί, από την πρώτη μέρα! Χρειάζεται όμως και η αντίστοιχη έμπνευση, πριν δούμε πώς θα την χρησιμοποιήσουμε.

Προφανώς, η γενική ιδέα είναι ότι από τη στιγμή που θα δεχτούμε μια βολή θα προβάλλουμε εναλλάξ την κανονική εικόνα του διαστημοπλοίου και τη δεύτερη, για κάποιο χρονικό διάστημα. Αλλά υπάρχουν προβλήματα…

  • Πόσο γρήγορα θα γίνεται η εναλλαγή; Αν αλλάζουμε εικόνα ανά καρέ που δημιουργούμε (η προγραμματιστικά εύκολη λύση) δεν θα βλέπουμε τίποτα! Μια αλλαγή που γίνεται 50-100 και παραπάνω φορές το δευτερόλεπτο είναι πρακτικά αόρατη. (Βλέπετε τις λάμπες στο σπίτι σας να αναβοσβήνουν;)
  • Πώς θα ρυθμίσουμε το συνολικό χρονικό διάστημα προβολής του εφέ; Θα πρέπει να ρυθμίσουμε είτε χρόνο είτε συνολικό αριθμό εναλλαγής των εικόνων, έως ότου επιστρέψουμε στην αρχική, μοναχική εικόνα.

Αφού αποφασίσαμε ότι δεν μπορούμε να εναλλάσσουμε εικόνα ανά καρέ και δεν μπορούμε να βασιστούμε στο framerate για το εφέ, υποπτευόμαστε ότι θα υπάρχει κάποιος τρόπος να το χρονομετρήσουμε. Ξέρουμε ήδη το γνωστό μας clock, το οποίο μας βοηθάει να κρατάμε σταθερή την ταχύτητα των αντικειμένων μας ανεξάρτητα από το framerate. Μήπως θέλουμε και εδώ κάτι αντίστοιχο;

Ευτυχώς για μας, το Pygame υποστηρίζει τη χρήση timers και μάλιστα πολλαπλών. Μπορούμε να ορίσουμε έναν timer με συγκεκριμένο χρόνο tick. Σε κάθε tick του timer θα λαμβάνουμε ένα event, αρκεί βέβαια να προσθέσουμε μερικές γραμμές στη διαχείριση events που ήδη έχουμε.

Αλλαγές στις βασικές κλάσεις
Έχοντας μια γενική ιδέα στο μυαλό μας, πριν προσπαθήσουμε να βάλουμε τον timer πρέπει να ασχοληθούμε με πιο πεζά θέματα. Βλέπετε, όπως έχουμε φτιάξει την κλάση craft δεν επιτρέπει παρά μόνο μια εικόνα για κάθε αντικείμενο, και ξαφνικά εμείς θέλουμε δύο! Ας μετατρέψουμε λοιπόν λίγο την κλάση craft:

<br />
class Craft(object):<br />
	def __init__ (self, imagefiles, coord):<br />
		self.shape = [pygame.image.load(imagefile) for imagefile in imagefiles]<br />
		self.ship_width = self.shape[0].get_width()<br />
		self.ship_height = self.shape[0].get_height()<br />
		self.rect = pygame.Rect(coord,(self.ship_width, self.ship_height))<br />
		self.ship_midwidth = self.ship_width / 2<br />
		self.firecolor=(255,0,0)<br />
		self.firespeed = -800<br />
		self.shotlength = 20<br />
	def Show(self, surface,imageindex):<br />
		surface.blit(self.shape[imageindex],(self.rect[0],self.rect[1]))<br />

Το self.shape είναι πλέον λίστα και φορτώνουμε σε αυτή όλες τις εικόνες από τα ονόματα αρχείων που περιέχει το imagefiles, χρησιμοποιώντας το πολύ βολικό list comprehension (το έχουμε ξαναδεί, ψάξτε λίγο στον κύριο βρόχο). Το imagefiles μπορεί να είναι λίστα ή tuple. Πρέπει όμως να δούμε και τις κλάσεις που έχουν την Craft ως γονική. Η Alien βέβαια θα λαμβάνει μόνο μια εικόνα. Αφού όμως έχουμε μετατρέψει τη γονική κλάση για να χειρίζεται λίστα από εικόνες, τι θα κάνουμε;

<br />
class Alien(Craft):<br />
	def __init__(self, imagefile, coord, speed_x, speed_y):<br />
		imagefiles = (imagefile,)<br />
		super(Alien, self).__init__(imagefiles, coord)<br />

Πήραμε τo ένα όνομα αρχείου εικόνας που θα λάβει ο constructor της Alien από το κύριο πρόγραμμα και το μετατρέψαμε σε tuple του… ενός στοιχείου, για να το δώσουμε στον γονικό constructor της Craft! Η αστεία αυτή μετατροπή έγινε με την εντολή

<br />
imagefiles = (imagefile,)<br />

όπου το κόμμα δείχνει ότι δημιουργούμε tuple με ένα μόνο στοιχείο. Δεν θα μπορούσαμε να χρησιμοποιήσουμε μόνο την παρένθεση, γιατί η Python δεν θα ήξερε αν προσπαθούμε να φτιάξουμε tuple ή παράσταση (όλα τα έχουν προβλέψει πια, αυτοί οι Python developers). Πρέπει να φτιάξουμε και μια συνάρτηση Show για το Alien class, το οποίο μέχρι τώρα χρησιμοποιούσε αυτούσιο την αντίστοιχη του Craft class:

<br />
def Show(self, surface):<br />
	imageindex = 0<br />
	super(Alien,self).Show(surface,imageindex)<br />

Τέλειο! Αφού το Alien έχει πάντα σταθερή εικόνα, το imageindex θα είναι πάντα μηδέν και θα δείχνει τη μια και μοναδική εικόνα που έχουμε δώσει. Ενώ στο δικό μας διαστημόπλοιο, μπορούμε να αλλάζουμε το imageindex κατά βούληση, αλλάζοντάς του έτσι και τη μορφή. Πάμε να δούμε τις αλλαγές στο κύριο πρόγραμμα. Έξω από το βρόχο ορίζουμε τις μεταβλητές imageindex και flashcount:

<br />
imageindex = 0<br />
flashcount = 0<br />

Η δημιουργία του SpaceShip αλλάζει ελαφρά, αφού πλέον θα του δώσουμε ένα tuple με δύο εικόνες:

<br />
shipimages = ('spaceship2.png', 'spaceship3.png')<br />
SpaceShip = SpaceCraft(shipimages,spaceship_pos, spaceship_low, spaceship_high,laser)<br />

Τέλος, το μόνο άλλο που αλλάζει είναι η κλήση της Show για το διαστημόπλοιό μας, μέσα στον κύριο βρόχο:

<br />
SpaceShip.Show(screen,imageindex)<br />

Προσθήκη του timer
To παιχνίδι μας πρέπει τώρα να λειτουργεί όπως πριν και μένει μόνο ο timer για την εναλλαγή των εικόνων. Ώρα να τον εγκαινιάσουμε:

<br />
if SpaceShip.rect.collidepoint(theshot.GetXY()) and not GameOver:<br />
	destroyed.play()<br />
	pygame.time.set_timer(USEREVENT+1,25)<br />
	shield.Decrease(25)<br />

Όταν χτυπηθούμε, ξεκινά ένας timer ο οποίος θα στέλνει το event USEREVENT+1 ανά 25 χιλιοστά του δευτερολέπτου. Το USEREVENT περιέχεται κι αυτό στο pygame.locals και, όπως φαντάζεστε, είναι η αρχή μιας σειράς αριθμών που δεν χρησιμοποιούνται κάπου αλλού και μπορούμε να τα χρησιμοποιήσουμε για τα δικά μας συμβάντα (μπορούμε να έχουμε USEREVENT+1, USEREVENT+2 κ.ο.κ.). Το μόνο που μένει τώρα είναι να αναβοσβήσουμε το διαστημόπλοιο μας δέκα φορές και μετά να απενεργοποιήσουμε το timer. Οι γραμμές προστίθενται στο βρόχο for, για τη διαχείριση των συμβάντων:

<br />
if event.type == USEREVENT+1:<br />
	if flashcount &lt; 10:<br />
		flashcount += 1<br />
		if imageindex == 1:<br />
			imageindex = 0<br />
		else:<br />
			imageindex = 1<br />
	else:<br />
		imageindex = 0<br />
		flashcount = 0<br />
		pygame.time.set_timer(USEREVENT+1,0)<br />

Κάθε φορά που λαμβάνουμε ένα event τύπου USEREVENT+1, ελέγχουμε το flashcount. Αν δεν έχει φτάσει ακόμα την τιμή 10, αλλάζουμε το imageindex από 0 σε 1 ή αντίστροφα. Όταν το flashcount γίνει 10, επιστρέφουμε στην κανονική εικόνα, μηδενίζουμε το flashcount και φυσικά απενεργοποιούμε το timer θέτοντάς του μηδέν στο χρόνο tick. Πανεύκολο!

Επεκτείνοντας το παιχνίδι: Μερικές βασικές ιδέες
Ο κώδικας είναι το καλύτερο παιχνίδι! Αν το πιστεύετε και εσείς, ορίστε μερικές ιδέες για να επεκτείνετε το Pygame Invaders…

  • Κάντε μερικούς εξωγήινους να ρίχνουν δωράκια, όταν τους σκοτώνετε: Power ups, αύξηση ασπίδας, πιο γρήγορο laser για το σκάφος σας. Πάρτε ιδέες από παιχνίδια όπως το Arkanoid.
  • Κάντε τη δυσκολία του παιχνιδιού να αλλάζει, όσο αυξάνονται τα κύματα των εξωγήινων. Μπορεί να ρίχνουν πιο συχνά, να κινούνται πιο γρήγορα ή να έχουν κάποιο συγκεκριμένο σχέδιο για την καταστροφή σας, αντί απλά να κάνουν bounce αριστερά-δεξιά.
  • Προσθέστε πίστες: Μετά από μερικά κύματα θα μπορούσατε να κάνετε ένα γρήγορο warp (κινούμενο background έχετε) και να βρίσκεστε κάπου αλλού, με διαφορετικούς εξωγήινους.
  • Βάλτε μια έξτρα πίστα με μετεωρίτες, αντί για εξωγήινους. Δείτε το Parsec του TI-99/4A στο YouTube.
  • Στα εξελιγμένα διαστημικά παιχνίδια, στο τέλος εμφανίζεται ο Μεγάλος Κακός (ΤΜ). Φτιάξτε μια πίστα με τον τεράστιο εξωγήινο που θα πρέπει να του ρίξετε lasers, phasers και το περίσσευμα της προχθεσινής πίτσας, για να πεθάνει!
  • Προσθέστε cheats για να παίζετε χωρίς να χάνετε, σε αντίθεση με τους φίλους σας (έπρεπε να το έχετε κάνει ήδη).
  • Στο διαστημόπλοιό μας έχουμε ήδη και κίνηση πάνω-κάτω, εκτός από αριστερά-δεξιά. Δεν ανιχνεύονται όμως συγκρούσεις μεταξύ του σκάφους μας και των εξωγήινων. Φτιάξτε το!

Θα χαρούμε πολύ να δούμε παραλλαγές και βελτιώσεις στο παιχνίδι μας — γιατί όχι και τα δικά σας παιχνίδια! Αν θέλετε μάλιστα μπορούμε να τα δημοσιεύσουμε στο PygameGR, από όπου μπορείτε να κατεβάσετε και τον πλήρη και τελικό (για εμάς!) κώδικα του Pygame Invaders.

Leave a Reply

You must be logged in to post a comment.

Σύνδεση

Αρχείο δημοσιεύσεων