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/4-collision-detection/README.md

23 KiB

Δημιουργία ενός Παιχνιδιού Διαστήματος Μέρος 4: Προσθήκη Λέιζερ και Ανίχνευση Συγκρούσεων

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

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

Σκεφτείτε τη στιγμή στον Πόλεμο των Άστρων όταν οι τορπίλες πρωτονίων του Λουκ χτύπησαν την έξοδο του Άστρου του Θανάτου. Αυτή η ακριβής ανίχνευση σύγκρουσης άλλαξε τη μοίρα του γαλαξία! Στα παιχνίδια, η ανίχνευση σύγκρουσης λειτουργεί με τον ίδιο τρόπο - καθορίζει πότε τα αντικείμενα αλληλεπιδρούν και τι συμβαίνει στη συνέχεια.

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

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

Κάντε λίγη έρευνα για το πρώτο παιχνίδι υπολογιστή που γράφτηκε ποτέ. Ποια ήταν η λειτουργικότητά του;

Ανίχνευση Συγκρούσεων

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

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

Αναπαράσταση Ορθογωνίου

Κάθε αντικείμενο του παιχνιδιού χρειάζεται συντεταγμένες ορίων, παρόμοια με τον τρόπο που το ρόβερ Mars Pathfinder χαρτογράφησε τη θέση του στην επιφάνεια του Άρη. Δείτε πώς ορίζουμε αυτές τις συντεταγμένες ορίων:

rectFromGameObject() {
  return {
    top: this.y,
    left: this.x,
    bottom: this.y + this.height,
    right: this.x + this.width
  }
}

Ας το αναλύσουμε:

  • Άνω άκρη: Είναι απλά το σημείο όπου ξεκινά το αντικείμενό σας κάθετα (η θέση y του)
  • Αριστερή άκρη: Το σημείο όπου ξεκινά οριζόντια (η θέση x του)
  • Κάτω άκρη: Προσθέστε το ύψος στη θέση y - τώρα ξέρετε πού τελειώνει!
  • Δεξιά άκρη: Προσθέστε το πλάτος στη θέση x - και έχετε τα πλήρη όρια.

Αλγόριθμος Τέμνουσας

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

function intersectRect(r1, r2) {
  return !(r2.left > r1.right ||
    r2.right < r1.left ||
    r2.top > r1.bottom ||
    r2.bottom < r1.top);
}

Η δοκιμή διαχωρισμού λειτουργεί όπως τα συστήματα ραντάρ:

  • Είναι το ορθογώνιο 2 εντελώς δεξιά από το ορθογώνιο 1;
  • Είναι το ορθογώνιο 2 εντελώς αριστερά από το ορθογώνιο 1;
  • Είναι το ορθογώνιο 2 εντελώς κάτω από το ορθογώνιο 1;
  • Είναι το ορθογώνιο 2 εντελώς πάνω από το ορθογώνιο 1;

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

Διαχείριση Κύκλου Ζωής Αντικειμένων

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

Δείτε πώς σημειώνουμε κάτι για αφαίρεση:

// Mark object for removal
enemy.dead = true;

Γιατί αυτή η προσέγγιση λειτουργεί:

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

Στη συνέχεια, φιλτράρουμε τα σημειωμένα αντικείμενα πριν από τον επόμενο κύκλο απόδοσης:

gameObjects = gameObjects.filter(go => !go.dead);

Τι κάνει αυτό το φιλτράρισμα:

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

Υλοποίηση Μηχανισμών Λέιζερ

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

Για να λειτουργήσει αυτό, πρέπει να συντονίσουμε μερικά διαφορετικά κομμάτια:

Βασικά στοιχεία για υλοποίηση:

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

Υλοποίηση Ελέγχου Ρυθμού Πυροδότησης

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

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

class Cooldown {
  constructor(time) {
    this.cool = false;
    setTimeout(() => {
      this.cool = true;
    }, time);
  }
}

class Weapon {
  constructor() {
    this.cooldown = null;
  }
  
  fire() {
    if (!this.cooldown || this.cooldown.cool) {
      // Create laser projectile
      this.cooldown = new Cooldown(500);
    } else {
      // Weapon is still cooling down
    }
  }
}

Πώς λειτουργεί το σύστημα ψύξης:

  • Όταν δημιουργείται, το όπλο ξεκινά "ζεστό" (δεν μπορεί να πυροδοτήσει ακόμα)
  • Μετά την περίοδο αναμονής, γίνεται "κρύο" (έτοιμο για πυροδότηση)
  • Πριν από την πυροδότηση, ελέγχουμε: "Είναι το όπλο κρύο;"
  • Αυτό αποτρέπει την υπερβολική πυροδότηση ενώ διατηρεί τους ελέγχους ανταποκρινόμενους

Ανατρέξτε στο μάθημα 1 της σειράς παιχνιδιών διαστήματος για να θυμηθείτε τα συστήματα ψύξης.

Δημιουργία Συστήματος Ανίχνευσης Συγκρούσεων

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

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

💡 Χρήσιμη Συμβουλή: Το sprite του λέιζερ περιλαμβάνεται ήδη στον φάκελο των πόρων σας και αναφέρεται στον κώδικα σας, έτοιμο για υλοποίηση.

Κανόνες Συγκρούσεων για Υλοποίηση

Μηχανισμοί παιχνιδιού για προσθήκη:

  1. Το λέιζερ χτυπά εχθρό: Το αντικείμενο του εχθρού καταστρέφεται όταν χτυπηθεί από λέιζερ
  2. Το λέιζερ χτυπά το όριο της οθόνης: Το λέιζερ αφαιρείται όταν φτάσει στην κορυφή της οθόνης
  3. Σύγκρουση εχθρού και ήρωα: Και τα δύο αντικείμενα καταστρέφονται όταν τέμνονται
  4. Ο εχθρός φτάνει στο κάτω μέρος: Κατάσταση τέλους παιχνιδιού όταν οι εχθροί φτάνουν στο κάτω μέρος της οθόνης

Ρύθμιση του Περιβάλλοντος Ανάπτυξης

Καλά νέα - έχουμε ήδη ετοιμάσει το μεγαλύτερο μέρος της βάσης για εσάς! Όλοι οι πόροι του παιχνιδιού σας και η βασική δομή είναι έτοιμοι στον υποφάκελο your-work, έτοιμοι για να προσθέσετε τις συναρπαστικές λειτουργίες ανίχνευσης συγκρούσεων.

Δομή Έργου

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

Κατανόηση της δομής των αρχείων:

  • Περιέχει όλες τις εικόνες sprite που χρειάζονται για τα αντικείμενα του παιχνιδιού
  • Περιλαμβάνει το κύριο έγγραφο HTML και το αρχείο εφαρμογής JavaScript
  • Παρέχει τη διαμόρφωση πακέτου για το τοπικό διακομιστή ανάπτυξης

Εκκίνηση του Διακομιστή Ανάπτυξης

Μεταβείτε στον φάκελο του έργου σας και ξεκινήστε τον τοπικό διακομιστή:

cd your-work
npm start

Αυτή η ακολουθία εντολών:

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

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

Υλοποίηση Βήμα-Βήμα

Όπως η συστηματική προσέγγιση που χρησιμοποίησε η NASA για να προγραμματίσει το διαστημόπλοιο Voyager, θα υλοποιήσουμε την ανίχνευση συγκρούσεων μεθοδικά, χτίζοντας κάθε στοιχείο βήμα-βήμα.

1. Προσθήκη Ορίων Συγκρούσεων Ορθογωνίου

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

rectFromGameObject() {
    return {
      top: this.y,
      left: this.x,
      bottom: this.y + this.height,
      right: this.x + this.width,
    };
  }

Αυτή η μέθοδος επιτυγχάνει:

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

2. Υλοποίηση Ανίχνευσης Τομής

Τώρα ας δημιουργήσουμε τον ντετέκτιβ συγκρούσεων - μια συνάρτηση που μπορεί να πει πότε δύο ορθογώνια επικαλύπτονται:

function intersectRect(r1, r2) {
  return !(
    r2.left > r1.right ||
    r2.right < r1.left ||
    r2.top > r1.bottom ||
    r2.bottom < r1.top
  );
}

Αυτός ο αλγόριθμος λειτουργεί με:

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

3. Υλοποίηση Συστήματος Πυροδότησης Λέιζερ

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

Σταθερές Μηνυμάτων

Πρώτα, ας ορίσουμε μερικούς τύπους μηνυμάτων ώστε τα διάφορα μέρη του παιχνιδιού να μπορούν να επικοινωνούν μεταξύ τους:

KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",

Αυτές οι σταθερές παρέχουν:

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

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

} else if(evt.keyCode === 32) {
  eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}

Αυτός ο χειριστής εισόδου:

  • Ανιχνεύει πατήματα πλήκτρων διαστήματος χρησιμοποιώντας το keyCode 32
  • Εκπέμπει ένα τυποποιημένο μήνυμα γεγονότος
  • Επιτρέπει αποσυνδεδεμένη λογική πυροδότησης
Ρύθμιση Ακροατή Γεγονότων

Καταχωρήστε τη συμπεριφορά πυροδότησης στη συνάρτηση initGame():

eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
 if (hero.canFire()) {
   hero.fire();
 }
});

Αυτός ο ακροατής γεγονότων:

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

Προσθέστε διαχείριση συγκρούσεων για τις αλληλεπιδράσεις λέιζερ-εχθρού:

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

Αυτός ο χειριστής συγκρούσεων:

  • Λαμβάνει δεδομένα γεγονότος σύγκρουσης με τα δύο αντικείμενα
  • Σημειώνει και τα δύο αντικείμενα για αφαίρεση
  • Εξασφαλίζει σωστό καθαρισμό μετά τη σύγκρουση

4. Δημιουργία Κλάσης Laser

Υλοποιήστε ένα λέιζερ που κινείται προς τα πάνω και διαχειρίζεται τον κύκλο ζωής του:

class Laser extends GameObject {
  constructor(x, y) {
    super(x, y);
    this.width = 9;
    this.height = 33;
    this.type = 'Laser';
    this.img = laserImg;
    
    let id = setInterval(() => {
      if (this.y > 0) {
        this.y -= 15;
      } else {
        this.dead = true;
        clearInterval(id);
      }
    }, 100);
  }
}

**Αυτή η υλοποίηση κ


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