You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/el/6-space-game/6-end-condition/README.md

24 KiB

Δημιουργία ενός Παιχνιδιού Διαστήματος Μέρος 6: Τέλος και Επανεκκίνηση

Κάθε σπουδαίο παιχνίδι χρειάζεται σαφείς συνθήκες τέλους και έναν ομαλό μηχανισμό επανεκκίνησης. Έχετε δημιουργήσει ένα εντυπωσιακό παιχνίδι διαστήματος με κίνηση, μάχες και βαθμολογία - τώρα είναι η ώρα να προσθέσετε τα τελικά κομμάτια που θα το κάνουν να φαίνεται ολοκληρωμένο.

Το παιχνίδι σας αυτή τη στιγμή τρέχει επ' αόριστον, όπως οι δορυφόροι Voyager που εκτοξεύτηκαν από τη NASA το 1977 - εξακολουθούν να ταξιδεύουν στο διάστημα δεκαετίες αργότερα. Ενώ αυτό είναι καλό για την εξερεύνηση του διαστήματος, τα παιχνίδια χρειάζονται καθορισμένα σημεία τέλους για να δημιουργήσουν ικανοποιητικές εμπειρίες.

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

Κουίζ Πριν το Μάθημα

Κουίζ πριν το μάθημα

Κατανόηση των Συνθηκών Τέλους του Παιχνιδιού

Πότε πρέπει να τελειώνει το παιχνίδι σας; Αυτή η θεμελιώδης ερώτηση έχει διαμορφώσει τον σχεδιασμό παιχνιδιών από την εποχή των πρώτων arcade παιχνιδιών. Το Pac-Man τελειώνει όταν σας πιάσουν τα φαντάσματα ή καθαρίσετε όλες τις κουκκίδες, ενώ το Space Invaders τελειώνει όταν οι εξωγήινοι φτάσουν στο κάτω μέρος ή όταν τους καταστρέψετε όλους.

Ως δημιουργός του παιχνιδιού, εσείς καθορίζετε τις συνθήκες νίκης και ήττας. Για το παιχνίδι διαστήματος μας, εδώ είναι μερικές αποδεδειγμένες προσεγγίσεις που δημιουργούν ενδιαφέρον gameplay:

  • Καταστροφή N εχθρικών σκαφών: Είναι αρκετά συνηθισμένο, αν χωρίσετε το παιχνίδι σε διαφορετικά επίπεδα, να χρειάζεται να καταστρέψετε N εχθρικά σκάφη για να ολοκληρώσετε ένα επίπεδο.
  • Καταστροφή του σκάφους σας: Υπάρχουν σίγουρα παιχνίδια όπου χάνετε το παιχνίδι αν καταστραφεί το σκάφος σας. Μια άλλη κοινή προσέγγιση είναι η έννοια των ζωών. Κάθε φορά που καταστρέφεται το σκάφος σας, αφαιρείται μια ζωή. Όταν χαθούν όλες οι ζωές, τότε χάνετε το παιχνίδι.
  • Συλλογή N πόντων: Μια άλλη κοινή συνθήκη τέλους είναι να συλλέξετε πόντους. Το πώς θα κερδίσετε πόντους εξαρτάται από εσάς, αλλά είναι αρκετά συνηθισμένο να αποδίδονται πόντοι σε διάφορες δραστηριότητες, όπως η καταστροφή ενός εχθρικού σκάφους ή η συλλογή αντικειμένων που πέφτουν όταν καταστρέφονται.
  • Ολοκλήρωση ενός επιπέδου: Αυτό μπορεί να περιλαμβάνει διάφορες συνθήκες, όπως X εχθρικά σκάφη που καταστράφηκαν, Y πόντους που συλλέχθηκαν ή ίσως τη συλλογή ενός συγκεκριμένου αντικειμένου.

Υλοποίηση Λειτουργικότητας Επανεκκίνησης Παιχνιδιού

Τα καλά παιχνίδια ενθαρρύνουν την επαναληψιμότητα μέσω ομαλών μηχανισμών επανεκκίνησης. Όταν οι παίκτες ολοκληρώνουν ένα παιχνίδι (ή ηττώνται), συχνά θέλουν να προσπαθήσουν ξανά αμέσως - είτε για να ξεπεράσουν το σκορ τους είτε για να βελτιώσουν την απόδοσή τους.

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

Σκέψη: Σκεφτείτε τα παιχνίδια που έχετε παίξει. Υπό ποιες συνθήκες τελειώνουν και πώς σας προτρέπουν να τα ξαναρχίσετε; Τι κάνει την εμπειρία επανεκκίνησης ομαλή και τι την καθιστά απογοητευτική;

Τι θα Δημιουργήσετε

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

Αυτά θα προσθέσουμε σήμερα:

  1. Συνθήκη νίκης: Καταστρέψτε όλους τους εχθρούς και απολαύστε μια σωστή γιορτή (το αξίζετε!)
  2. Συνθήκη ήττας: Χάστε όλες τις ζωές σας και αντιμετωπίστε την ήττα με μια οθόνη αποτυχίας
  3. Μηχανισμός επανεκκίνησης: Πατήστε Enter για να ξαναρχίσετε - γιατί ένα παιχνίδι δεν είναι ποτέ αρκετό
  4. Διαχείριση κατάστασης: Καθαρή αρχή κάθε φορά - χωρίς υπολείμματα εχθρών ή περίεργα σφάλματα από το προηγούμενο παιχνίδι

Ξεκινώντας

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

Το έργο σας θα πρέπει να μοιάζει κάπως έτσι:

-| assets
  -| enemyShip.png
  -| player.png
  -| laserRed.png
  -| life.png
-| index.html
-| app.js
-| package.json

Ξεκινήστε τον διακομιστή ανάπτυξης:

cd your-work
npm start

Αυτή η εντολή:

  • Εκτελεί έναν τοπικό διακομιστή στο http://localhost:5000
  • Εξυπηρετεί σωστά τα αρχεία σας
  • Ανανεώνεται αυτόματα όταν κάνετε αλλαγές

Ανοίξτε το http://localhost:5000 στον περιηγητή σας και επιβεβαιώστε ότι το παιχνίδι σας λειτουργεί. Θα πρέπει να μπορείτε να κινείστε, να πυροβολείτε και να αλληλεπιδράτε με τους εχθρούς. Μόλις επιβεβαιωθεί, μπορούμε να προχωρήσουμε στην υλοποίηση.

💡 Χρήσιμη Συμβουλή: Για να αποφύγετε προειδοποιήσεις στο Visual Studio Code, δηλώστε το gameLoopId στην κορυφή του αρχείου σας ως let gameLoopId; αντί να το δηλώσετε μέσα στη λειτουργία window.onload. Αυτό ακολουθεί τις σύγχρονες βέλτιστες πρακτικές δήλωσης μεταβλητών της JavaScript.

Βήματα Υλοποίησης

Βήμα 1: Δημιουργία Λειτουργιών Παρακολούθησης Συνθηκών Τέλους

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

function isHeroDead() {
  return hero.life <= 0;
}

function isEnemiesDead() {
  const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
  return enemies.length === 0;
}

Τι συμβαίνει στο παρασκήνιο:

  • Ελέγχει αν ο ήρωάς μας έχει χάσει όλες τις ζωές του (ωχ!)
  • Μετράει πόσοι εχθροί είναι ακόμα ζωντανοί
  • Επιστρέφει true όταν το πεδίο μάχης είναι καθαρό από εχθρούς
  • Χρησιμοποιεί απλή λογική true/false για να διατηρεί τα πράγματα απλά
  • Φιλτράρει όλα τα αντικείμενα του παιχνιδιού για να βρει τους επιζώντες

Βήμα 2: Ενημέρωση Χειριστών Συμβάντων για τις Συνθήκες Τέλους

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

eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
    first.dead = true;
    second.dead = true;
    hero.incrementPoints();

    if (isEnemiesDead()) {
      eventEmitter.emit(Messages.GAME_END_WIN);
    }
});

eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
    enemy.dead = true;
    hero.decrementLife();
    if (isHeroDead())  {
      eventEmitter.emit(Messages.GAME_END_LOSS);
      return; // loss before victory
    }
    if (isEnemiesDead()) {
      eventEmitter.emit(Messages.GAME_END_WIN);
    }
});

eventEmitter.on(Messages.GAME_END_WIN, () => {
    endGame(true);
});
  
eventEmitter.on(Messages.GAME_END_LOSS, () => {
  endGame(false);
});

Τι συμβαίνει εδώ:

  • Η ακτίνα λέιζερ χτυπά εχθρό: Και οι δύο εξαφανίζονται, κερδίζετε πόντους και ελέγχουμε αν κερδίσατε
  • Ο εχθρός σας χτυπά: Χάνετε μια ζωή και ελέγχουμε αν είστε ακόμα ζωντανοί
  • Έξυπνη σειρά: Ελέγχουμε πρώτα για ήττα (κανείς δεν θέλει να κερδίσει και να χάσει ταυτόχρονα!)
  • Άμεσες αντιδράσεις: Μόλις συμβεί κάτι σημαντικό, το παιχνίδι το γνωρίζει

Βήμα 3: Προσθήκη Νέων Σταθερών Μηνυμάτων

Θα χρειαστεί να προσθέσετε νέους τύπους μηνυμάτων στο αντικείμενο Messages σας. Αυτές οι σταθερές βοηθούν στη διατήρηση της συνέπειας και αποτρέπουν τα τυπογραφικά λάθη στο σύστημα συμβάντων σας.

GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",

Στα παραπάνω, έχουμε:

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

Βήμα 4: Υλοποίηση Ελέγχων Επανεκκίνησης

Τώρα θα προσθέσετε ελέγχους πληκτρολογίου που επιτρέπουν στους παίκτες να επανεκκινήσουν το παιχνίδι. Το πλήκτρο Enter είναι μια φυσική επιλογή, καθώς συνδέεται συνήθως με την επιβεβαίωση ενεργειών και την έναρξη νέων παιχνιδιών.

Προσθέστε ανίχνευση του πλήκτρου Enter στον υπάρχοντα ακροατή συμβάντων keydown:

else if(evt.key === "Enter") {
   eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}

Προσθέστε τη νέα σταθερά μηνύματος:

KEY_EVENT_ENTER: "KEY_EVENT_ENTER",

Τι πρέπει να γνωρίζετε:

  • Επεκτείνει το υπάρχον σύστημα χειρισμού συμβάντων πληκτρολογίου
  • Χρησιμοποιεί το πλήκτρο Enter ως ενεργοποιητή επανεκκίνησης για διαισθητική εμπειρία χρήστη
  • Εκπέμπει ένα προσαρμοσμένο συμβάν που μπορούν να ακούσουν άλλα μέρη του παιχνιδιού σας
  • Διατηρεί το ίδιο μοτίβο με τους άλλους ελέγχους πληκτρολογίου σας

Βήμα 5: Δημιουργία Συστήματος Εμφάνισης Μηνυμάτων

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

Δημιουργήστε τη λειτουργία displayMessage():

function displayMessage(message, color = "red") {
  ctx.font = "30px Arial";
  ctx.fillStyle = color;
  ctx.textAlign = "center";
  ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}

Βήμα προς βήμα, τι συμβαίνει:

  • Ορίζει το μέγεθος και την οικογένεια γραμματοσειράς για καθαρό, ευανάγνωστο κείμενο
  • Εφαρμόζει μια παράμετρο χρώματος με προεπιλογή το "κόκκινο" για προειδοποιήσεις
  • Κεντράρει το κείμενο οριζόντια και κάθετα στον καμβά
  • Χρησιμοποιεί σύγχρονες προεπιλεγμένες παραμέτρους JavaScript για ευέλικτες επιλογές χρώματος
  • Αξιοποιεί το πλαίσιο 2D του καμβά για άμεση απόδοση κειμένου

Δημιουργήστε τη λειτουργία endGame():

function endGame(win) {
  clearInterval(gameLoopId);

  // Set a delay to ensure any pending renders complete
  setTimeout(() => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = "black";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    if (win) {
      displayMessage(
        "Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
        "green"
      );
    } else {
      displayMessage(
        "You died !!! Press [Enter] to start a new game Captain Pew Pew"
      );
    }
  }, 200)  
}

Τι κάνει αυτή η λειτουργία:

  • Παγώνει τα πάντα στη θέση τους - κανένα άλλο κινούμενο σκάφος ή λέιζερ
  • Παίρνει μια μικρή παύση (200ms) για να αφήσει το τελευταίο καρέ να ολοκληρωθεί
  • Καθαρίζει την οθόνη και τη βάφει μαύρη για δραματικό αποτέλεσμα
  • Εμφανίζει διαφορετικά μηνύματα για νικητές και ηττημένους
  • Χρωματίζει τα νέα - πράσινο για καλά, κόκκινο για... όχι τόσο καλά
  • Λέει στους παίκτες ακριβώς πώς να ξαναρχίσουν

Βήμα 6: Υλοποίηση Λειτουργικότητας Επαναφοράς Παιχνιδιού

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

Δημιουργήστε τη λειτουργία resetGame():

function resetGame() {
  if (gameLoopId) {
    clearInterval(gameLoopId);
    eventEmitter.clear();
    initGame();
    gameLoopId = setInterval(() => {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.fillStyle = "black";
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      drawPoints();
      drawLife();
      updateGameObjects();
      drawGameObjects(ctx);
    }, 100);
  }
}

Ας κατανοήσουμε κάθε μέρος:

  • Ελέγχει αν υπάρχει τρέχων βρόχος παιχνιδιού πριν από την επαναφορά
  • Καθαρίζει τον υπάρχοντα βρόχο παιχνιδιού για να σταματήσει όλη την τρέχουσα δραστηριότητα
  • Αφαιρεί όλους τους ακροατές συμβάντων για να αποτρέψει διαρροές μνήμης
  • Επαναρχικοποιεί την κατάσταση του παιχνιδιού με νέα αντικείμενα και μεταβλητές
  • Ξεκινά έναν νέο βρόχο παιχνιδιού με όλες τις βασικές λειτουργίες
  • Διατηρεί το ίδιο διάστημα 100ms για συνεπή απόδοση παιχνιδιού

Προσθέστε τον ακροατή συμβάντων του πλήκτρου Enter στη λειτουργία initGame():

eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
  resetGame();
});

Προσθέστε τη μέθοδο clear() στην κλάση EventEmitter:

clear() {
  this.listeners = {};
}

Σημεία που πρέπει να θυμάστε:

  • Συνδέει το πάτημα του πλήκτρου Enter με τη λειτουργικότητα επαναφοράς παιχνιδιού
  • Καταχωρεί αυτόν τον ακροατή συμβάντων κατά την αρχικοποίηση του παιχνιδιού
  • Παρέχει έναν καθαρό τρόπο αφαίρεσης όλων των ακροατών συμβάντων κατά την επαναφορά
  • Αποτρέπει διαρροές μνήμης καθαρίζοντας τους χειριστές συμβάντων μεταξύ παιχνιδιών
  • Επαναφέρει το αντικείμενο ακροατών σε κενή κατάσταση για φρέσκια αρχικοποίηση

Συγχαρητήρια! 🎉

👽 💥 🚀 Έχετε δημιουργήσει με επιτυχία ένα ολοκληρωμένο παιχνίδι από την αρχή. Όπως οι προγραμματιστές που δημιούργησαν τα πρώτα βιντεοπαιχνίδια τη δεκαετία του 1970, έχετε μετατρέψει γραμμές κώδικα σε μια διαδραστική εμπειρία με σωστούς μηχανισμούς παιχνιδιού και ανατροφοδότηση χρήστη. 🚀 💥 👽

Έχετε επιτύχει:

  • Υλοποίηση πλήρων συνθηκών νίκης και ήττας με ανατροφοδότηση χρήστη
  • Δημιουργία ενός ομαλού συστήματος επανεκκίνησης για συνεχή gameplay
  • Σχεδίαση καθαρής οπτικής επικοινωνίας για τις καταστάσεις του παιχνιδιού
  • Διαχείριση σύνθετων μεταβάσεων κατάστασης παιχνιδιού και καθαρισμού
  • Συναρμολόγηση όλων των στοιχείων σε ένα συνεκτικό, παικτικό παιχνίδι

Πρόκληση Git


Αποποίηση ευθύνης:
Αυτό το έγγραφο έχει μεταφραστεί χρησιμοποιώντας την υπηρεσία μετάφρασης AI Co-op Translator. Παρόλο που καταβάλλουμε προσπάθειες για ακρίβεια, παρακαλούμε να έχετε υπόψη ότι οι αυτοματοποιημένες μεταφράσεις ενδέχεται να περιέχουν λάθη ή ανακρίβειες. Το πρωτότυπο έγγραφο στη μητρική του γλώσσα θα πρέπει να θεωρείται η αυθεντική πηγή. Για κρίσιμες πληροφορίες, συνιστάται επαγγελματική ανθρώπινη μετάφραση. Δεν φέρουμε ευθύνη για τυχόν παρεξηγήσεις ή εσφαλμένες ερμηνείες που προκύπτουν από τη χρήση αυτής της μετάφρασης.