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

Python Game Programming (Part 6.5)

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

Σημείωση: Το παρόν post αποτελεί συμπλήρωμα άρθρου που δημοσιεύεται στο τεύχος 010 του deltaHacker, στα πλαίσια της σειράς για την εκμάθηση της Python μέσω της κατασκευής των δικών μας computer games.

Απλούστευση του SpaceBackground
Είναι πολύ απλό. Ο constructor θα περιέχει μόνο τις παρακάτω γραμμές:

class SpaceBackground:
	def __init__(self, screenheight, imagefile):
		self.shape = pygame.image.load(imagefile)
		self.coord = [0,0]
		self.coord2 = [0, -screenheight]
		self.y_original = self.coord[1]
		self.y2_original = self.coord2[1]

Πλέον, στο κύριο πρόγραμμά μας η μόνη παράμετρος που περνάμε όσον αφορά στις συντεταγμένες είναι το screenheight:

StarField = SpaceBackground(screenheight, "stars.jpg")

Βελτιστοποίηση εμφάνισης της βολής laser
Παρατηρήσατε και εσείς φυσικά ότι η βολή του laser δεν ξεκινάει ακριβώς από τη μύτη του διαστημοπλοίου, αλλά ότι έχει offset λίγες γραμμές. Λογικό, καθώς γύρω από το διαστημόπλοιο μας βρίσκεται ένα αόρατο κουτάκι που το περιέχει και η βολή εμφανίζεται από την πάνω γραμμή του, η οποία δεν συμπίπτει αναγκαστικά με τη μύτη του κανονιού. Θα πρέπει να εμφανίζουμε το laser από λίγο πιο κάτω.

Για να το κάνουμε αυτό με τον Σωστό Τρόπο (TM), θα δώσουμε στο laser μια επιπλέον παράμετρο: την voffset (vertical offset). Μην ξεχνάτε ότι το laser θα το χρησιμοποιήσουμε και για τους εξωγήινους κι εκεί τα πράγματα ίσως είναι διαφορετικά! Ο νέος μας constructor θα είναι κάπως έτσι:

class Laser:
	def __init__(self, coord, color, size, speed, refline, voffset):
		self.x1 = coord[0]
		self.y1 = coord[1] + voffset
		self.size = size
		self.color = color
		self.speed = speed
		self.refline = refline

Και φυσικά η κλήση που κάνουμε διαμορφώνεται κάπως έτσι:

shot = Laser((self.rect[0]+self.ship_midwidth, self.rect[1]),self.firecolor,self.shotlength,self.firespeed,self.rect[1],15)

Αυτό το μαγικό 15, είναι το offset που επέλεξα για το διαστημόπλοιο μας. Αν βέβαια θέλουμε να είμαστε σωστοί, το offset θα το βάλουμε σε μια μεταβλητή self στον constructor. Θα μου πείτε τώρα γιατί να μην κάνουμε κάτι αντίστοιχο και για το midwidth;! Σκεφτείτε το λίγο… :)

Δημιουργία συνάρτησης Fire στο SpaceCraft Class
Σκοπός μας εδώ είναι να μεταφέρουμε τον ήχο από τη γονική συνάρτηση, ώστε να εξασφαλίσουμε ότι μόνο το δικό μας (δυνατό) laser θα ακούγεται κι όχι οι βολές των κακόμοιρων, αδύναμων και χαμένων από χέρι εξωγήινων! Και ωραία, πήγατε στο Fire του Craft class και βγάλατε το laser.play(). Ποια θα είναι η συνάρτηση στο δικό μας SpaceCraft class;

def Fire(self):
	laser.play()
	return super(SpaceCraft,self).Fire()

Υπάρχει μόνο ένα πονηρό σημείο: Φυσικά και θα καλέσουμε με την super τη γονική συνάρτηση. Αλλά μην ξεχνάτε ότι αυτή επιστρέφει ένα αντικείμενο τύπου laser, το οποίο θα χρησιμοποιήσουμε αργότερα. Αν την καλέσετε απλώς έτσι

super(SpaceCraft,self).Fire()

Η δική μας fire δεν θα επιστρέψει κάτι. Θα προσπαθείτε μετά να προσθέσετε ένα αντικείμενο του τύπου… τίποτα, στη λίστα firelist που περιέχει τις βολές. Αυτό δεν είναι καθόλου καλό και θα δείτε την Python να παραπονιέται με το παρακάτω μήνυμα:

Traceback (most recent call last):
  File "pygame-invaders.py", line 210, in
    theshot.Move(time)
AttributeError: 'NoneType' object has no attribute 'Move'

Θα μπορούσατε φυσικά να καλέσετε την fire κι έτσι:

def Fire(self):
	laser.play()
	theshot = super(SpaceCraft,self).Fire()
	return theshot

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

Έρχονται οι Εξωγήινοι!
Έχουμε κίνηση, laser, scrolling background… Καιρός να βάλουμε και τους εξωγήινους στο παιχνίδι, δεν νομίζετε; Όχι τίποτα άλλο, να έχουμε κάτι να πυροβολούμε άμεσα στο deltaHacker Αυγούστου. Πάμε λοιπόν να δούμε πώς θα τους εμφανίσουμε.

Όπως είχαμε πει στην αρχική περιγραφή του παιχνιδιού, οι εξωγήινοι θα έρχονται σε κύματα. Καθώς ξεμπερδεύουμε με το ένα κύμα θα εμφανίζεται το επόμενο. Μια απλή σκέψη είναι φυσικά το κάθε κύμα να έχει ένα συγκεκριμένο αριθμό από εξωγήινους. Καθένας θα εμφανίζεται σε μια τυχαία θέση στην οθόνη μας και θα κινείται με σχετικά τυχαίες ταχύτητες, κάνοντας bounce στα όρια της οθόνης. (Σας θυμίζει κάτι;) Με βάση τα παραπάνω, μπορούμε να γράψουμε εύκολα μια αρχική υλοποίηση για την κλάση τους:

class Alien(Craft):
	def __init__(self, imagefile, coord, speed_x, speed_y):
		super(Alien, self).__init__(imagefile, coord)
		self.speed_x = speed_x
		self.speed_y = speed_y
		self.shot_height = 10
		self.firebaseline = self.ship_height
		self.firecolor=(255,255,0)
		self.firespeed = 200
	def Move(self, time):
		super(Alien,self).Move(self.speed_x, self.speed_y,time)
		if self.rect[0] >= 440 or self.rect[0] <= 10:
			self.speed_x = -self.speed_x
		if self.rect&#91;1&#93; <= 10 or self.rect&#91;1&#93; >= 440:
			self.speed_y = -self.speed_y

Τι παραπάνω έχει το Alien class από μας; Παρατηρήστε ότι ο constructor έχει από την αρχή τιμές για τις ταχύτητες του εξωγήινου και στους δύο άξονες — όπως είπαμε οι εξωγήινοι δεν είναι πολύ έξυπνοι. Ξεκινάνε με τυχαίες ταχύτητες τις οποίες θα παράγουμε στον κύριο βρόχο του παιχνιδιού. Δείτε τι γίνεται στη συνάρτηση Move. Όταν φτάσουν σε κάποια όρια της οθόνης (τα οποία *κακώς* είναι hardwired στον κώδικα, κάτι που θα διορθώσετε) απλώς αλλάζουν φορά κίνησης (εμ, αυτό το bouncing ball…). Εμείς βέβαια θα κανονίσουμε να έρχονται γενικά προς το μέρος μας, με τις αντίστοιχες εντολές στον κύριο βρόχο.

Πριν το while, όμως, θα πρέπει να ετοιμάσουμε τις εικόνες που θα χρησιμοποιηθούν. Οι εξωγήινοι είναι πολλών ειδών:

alienimage=('alien1.png','alien2.png','alien3.png','alien4.png','alien5.png')

Ορίζουμε και μια μεταβλητή που μας επιτρέπει να καθορίσουμε πόσοι εξωγήινοι θα υπάρχουν σε κάθε κύμα:

numofaliens = 8

Όπως κάναμε με τις βολές laser, θα αποθηκεύουμε τα αντικείμενα τύπου Alien που θα φτιάχνουμε σε μια λίστα η οποία αρχικά, έξω από το βρόχο, θα είναι κενή:

AlienShips = []

Μέσα στο βρόχο τώρα, θα χρησιμοποιήσουμε μια εντολή for για να δημιουργήσουμε το κύμα των χαζοεξωγήινων. Μπορείτε να βάλετε τις παρακάτω εντολές ακριβώς κάτω από την γραμμή time (προσέξτε το indentation):

if not AlienShips:
	# AlienShips empty means we need to create new 'wave'
	AlienShips = [ Alien(alienimage[randint(0,len(alienimage)-1)],[randint(20,screenwidth-80),randint(20,screenheight-140)],randint(100,150), randint(100,150)) for i in range(0,numofaliens) ]

Αν ψάχνετε το for, είναι λίγο κρυμμένο: Χρησιμοποιήσαμε μια δυνατότητα της Python που ονομάζεται list comprehension και μας επιτρέπει πολύ εύκολα να δημιουργήσουμε μια λίστα ενσωματώνοντας την εντολή for με τον τρόπο που βλέπετε παραπάνω (σκρολάρετε δεξιά). Θα μπορούσαμε βέβαια να το γράψουμε με τον κλασικό τρόπο:

for i in range(0,numofaliens):
	Alienships.append(Alien(alienimage[randint(0,len(alienimage)-1)],[randint(20,screenwidth-80),randint(20,screenheight-140)],randint(100,150), randint(100,150)))

Όπως παρατηρήσατε, χρησιμοποιούμε την randint για να παράγουμε αρχικές θέσεις και ταχύτητες για τους εξωγήινους. Οι τιμές έχουν επιλεγεί κατάλληλα για να μην πέφτουν εκτός οθόνης αλλά και να μην εμφανιστεί κανείς πάνω στο διαστημόπλοιο μας, στο κάτω μέρος της οθόνης.

Γιατί υπάρχει το if not AlienShips; Αν η λίστα AlienShips είναι άδεια, μπορεί να συμβαίνουν δύο πράγματα:

– μόλις ξεκινήσαμε το παιχνίδι, οπότε αυτό είναι το πρώτο κύμα εξωγήινων
– μόλις ξεμπερδέψαμε με όλους τους εξωγήινους του προηγούμενου κύματος

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

Φυσικά, οι εξωγήινοι κάπου πρέπει να φανούν και να κινηθούν. Οπότε θα έχουμε και το παρακάτω for. Προσέξτε ότι πρέπει να είναι μετά το Show για το κινούμενο φόντο μας:

For AlienShip in AlienShips:
	AlienShip.Show(screen)
	AlienShip.Move(time)

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

Leave a Reply

You must be logged in to post a comment.

Σύνδεση

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