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/1-introduction/README.md

27 KiB

Δημιουργία ενός Παιχνιδιού Διαστήματος Μέρος 1: Εισαγωγή

Κινούμενη εικόνα παιχνιδιού διαστήματος που δείχνει gameplay

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

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

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

Ερωτηματολόγιο πριν τη διάλεξη

Ερωτηματολόγιο πριν τη διάλεξη

Κληρονομικότητα και Σύνθεση στην Ανάπτυξη Παιχνιδιών

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

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

Ένα από τα πιο διάσημα βιβλία προγραμματισμού που έχουν γραφτεί ποτέ αφορά τα μοτίβα σχεδίασης.

Σε οποιοδήποτε παιχνίδι, υπάρχουν game objects τα διαδραστικά στοιχεία που γεμίζουν τον κόσμο του παιχνιδιού. Ήρωες, εχθροί, power-ups και οπτικά εφέ είναι όλα game objects. Καθένα υπάρχει σε συγκεκριμένες συντεταγμένες της οθόνης χρησιμοποιώντας τιμές x και y, παρόμοια με την απεικόνιση σημείων σε ένα καρτεσιανό επίπεδο.

Παρά τις οπτικές τους διαφορές, αυτά τα αντικείμενα συχνά μοιράζονται θεμελιώδεις συμπεριφορές:

  • Υπάρχουν κάπου Κάθε αντικείμενο έχει συντεταγμένες x και y ώστε το παιχνίδι να ξέρει πού να το σχεδιάσει
  • Πολλά μπορούν να κινούνται Οι ήρωες τρέχουν, οι εχθροί κυνηγούν, οι σφαίρες πετούν στην οθόνη
  • Έχουν διάρκεια ζωής Κάποια παραμένουν για πάντα, άλλα (όπως οι εκρήξεις) εμφανίζονται για λίγο και εξαφανίζονται
  • Αντιδρούν σε πράγματα Όταν συγκρούονται, τα power-ups συλλέγονται, οι μπάρες υγείας ενημερώνονται

Σκεφτείτε ένα παιχνίδι όπως το Pac-Man. Μπορείτε να εντοπίσετε τους τέσσερις τύπους αντικειμένων που αναφέρονται παραπάνω σε αυτό το παιχνίδι;

Έκφραση Συμπεριφοράς Μέσω Κώδικα

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

Η Προσέγγιση με Βάση τις Κλάσεις

Οι κλάσεις και η κληρονομικότητα παρέχουν μια δομημένη προσέγγιση για την οργάνωση των game objects. Όπως το ταξινομικό σύστημα που αναπτύχθηκε από τον Carl Linnaeus, ξεκινάτε με μια βασική κλάση που περιέχει κοινές ιδιότητες και στη συνέχεια δημιουργείτε εξειδικευμένες κλάσεις που κληρονομούν αυτά τα θεμελιώδη στοιχεία ενώ προσθέτουν συγκεκριμένες δυνατότητες.

Η κληρονομικότητα είναι μια σημαντική έννοια για να κατανοήσετε. Μάθετε περισσότερα στο άρθρο του MDN για την κληρονομικότητα.

Να πώς μπορείτε να υλοποιήσετε game objects χρησιμοποιώντας κλάσεις και κληρονομικότητα:

// Step 1: Create the base GameObject class
class GameObject {
  constructor(x, y, type) {
    this.x = x;
    this.y = y;
    this.type = type;
  }
}

Ας το αναλύσουμε βήμα προς βήμα:

  • Δημιουργούμε ένα βασικό πρότυπο που μπορεί να χρησιμοποιήσει κάθε game object
  • Ο constructor αποθηκεύει πού βρίσκεται το αντικείμενο (x, y) και τι είδους πράγμα είναι
  • Αυτό γίνεται η βάση πάνω στην οποία θα χτιστούν όλα τα game objects σας
// Step 2: Add movement capability through inheritance
class Movable extends GameObject {
  constructor(x, y, type) {
    super(x, y, type); // Call parent constructor
  }

  // Add the ability to move to a new position
  moveTo(x, y) {
    this.x = x;
    this.y = y;
  }
}

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

  • Επεκτείνει την κλάση GameObject για να προσθέσουμε λειτουργικότητα κίνησης
  • Καλέσει τον constructor του γονέα χρησιμοποιώντας super() για να αρχικοποιήσουμε τις κληρονομημένες ιδιότητες
  • Προσθέσει μια μέθοδο moveTo() που ενημερώνει τη θέση του αντικειμένου
// Step 3: Create specific game object types
class Hero extends Movable {
  constructor(x, y) {
    super(x, y, 'Hero'); // Set type automatically
  }
}

class Tree extends GameObject {
  constructor(x, y) {
    super(x, y, 'Tree'); // Trees don't need movement
  }
}

// Step 4: Use your game objects
const hero = new Hero(0, 0);
hero.moveTo(5, 5); // Hero can move!

const tree = new Tree(10, 15);
// tree.moveTo() would cause an error - trees can't move

Κατανοώντας αυτές τις έννοιες:

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

Αφιερώστε λίγα λεπτά για να ξανασκεφτείτε έναν ήρωα του Pac-Man (Inky, Pinky ή Blinky, για παράδειγμα) και πώς θα γραφόταν σε JavaScript.

Η Προσέγγιση της Σύνθεσης

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

// Step 1: Create base behavior objects
const gameObject = {
  x: 0,
  y: 0,
  type: ''
};

const movable = {
  moveTo(x, y) {
    this.x = x;
    this.y = y;
  }
};

Τι κάνει αυτός ο κώδικας:

  • Ορίζει ένα βασικό gameObject με ιδιότητες θέσης και τύπου
  • Δημιουργεί ένα ξεχωριστό αντικείμενο συμπεριφοράς movable με λειτουργικότητα κίνησης
  • Διαχωρίζει τις ανησυχίες διατηρώντας τα δεδομένα θέσης και τη λογική κίνησης ανεξάρτητα
// Step 2: Compose objects by combining behaviors
const movableObject = { ...gameObject, ...movable };

// Step 3: Create factory functions for different object types
function createHero(x, y) {
  return {
    ...movableObject,
    x,
    y,
    type: 'Hero'
  };
}

function createStatic(x, y, type) {
  return {
    ...gameObject,
    x,
    y,
    type
  };
}

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

  • Συνδυάσει τις βασικές ιδιότητες του αντικειμένου με τη συμπεριφορά κίνησης χρησιμοποιώντας τη σύνταξη spread
  • Δημιουργήσει factory functions που επιστρέφουν προσαρμοσμένα αντικείμενα
  • Επιτρέψει ευέλικτη δημιουργία αντικειμένων χωρίς αυστηρές ιεραρχίες κλάσεων
  • Δώσει στα αντικείμενα ακριβώς τις συμπεριφορές που χρειάζονται
// Step 4: Create and use your composed objects
const hero = createHero(10, 10);
hero.moveTo(5, 5); // Works perfectly!

const tree = createStatic(0, 0, 'Tree');
// tree.moveTo() is undefined - no movement behavior was composed

Σημαντικά σημεία για να θυμάστε:

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

**Which Pattern Should You Choose?**

> 💡 **Pro Tip**: Both patterns have their place in modern JavaScript development. Classes work well for clearly defined hierarchies, while composition shines when you need maximum flexibility.
> 
**Here's when to use each approach:**
- **Choose** inheritance when you have clear "is-a" relationships (a Hero *is-a* Movable object)
- **Select** composition when you need "has-a" relationships (a Hero *has* movement abilities)
- **Consider** your team's preferences and project requirements
- **Remember** that you can mix both approaches in the same application

## Communication Patterns: The Pub/Sub System

As applications grow complex, managing communication between components becomes challenging. The publish-subscribe pattern (pub/sub) solves this problem using principles similar to radio broadcasting  one transmitter can reach multiple receivers without knowing who's listening.

Consider what happens when a hero takes damage: the health bar updates, sound effects play, visual feedback appears. Rather than coupling the hero object directly to these systems, pub/sub allows the hero to broadcast a "damage taken" message. Any system that needs to respond can subscribe to this message type and react accordingly.

✅ **Pub/Sub** stands for 'publish-subscribe'

### Understanding the Pub/Sub Architecture

The pub/sub pattern keeps different parts of your application loosely coupled, meaning they can work together without being directly dependent on each other. This separation makes your code more maintainable, testable, and flexible to changes.

**The key players in pub/sub:**
- **Messages**  Simple text labels like `'PLAYER_SCORED'` that describe what happened (plus any extra info)
- **Publishers**  The objects that shout out "Something happened!" to anyone who's listening
- **Subscribers**  The objects that say "I care about that event" and react when it happens
- **Event System**  The middleman that makes sure messages get to the right listeners

### Building an Event System

Let's create a simple but powerful event system that demonstrates these concepts:

```javascript
// Step 1: Create the EventEmitter class
class EventEmitter {
  constructor() {
    this.listeners = {}; // Store all event listeners
  }
  
  // Register a listener for a specific message type
  on(message, listener) {
    if (!this.listeners[message]) {
      this.listeners[message] = [];
    }
    this.listeners[message].push(listener);
  }
  
  // Send a message to all registered listeners
  emit(message, payload = null) {
    if (this.listeners[message]) {
      this.listeners[message].forEach(listener => {
        listener(message, payload);
      });
    }
  }
}

Αναλύοντας τι συμβαίνει εδώ:

  • Δημιουργεί ένα κεντρικό σύστημα διαχείρισης γεγονότων χρησιμοποιώντας μια απλή κλάση
  • Αποθηκεύει listeners σε ένα αντικείμενο οργανωμένο ανά τύπο μηνύματος
  • Καταχωρεί νέους listeners χρησιμοποιώντας τη μέθοδο on()
  • Μεταδίδει μηνύματα σε όλους τους ενδιαφερόμενους listeners χρησιμοποιώντας emit()
  • Υποστηρίζει προαιρετικά δεδομένα για τη μεταφορά σχετικών πληροφοριών

Συνδυάζοντας τα Όλα: Ένα Πρακτικό Παράδειγμα

Ας το δούμε στην πράξη! Θα δημιουργήσουμε ένα απλό σύστημα κίνησης που δείχνει πόσο καθαρό και ευέλικτο μπορεί να είναι το pub/sub:

// Step 1: Define your message types
const Messages = {
  HERO_MOVE_LEFT: 'HERO_MOVE_LEFT',
  HERO_MOVE_RIGHT: 'HERO_MOVE_RIGHT',
  ENEMY_SPOTTED: 'ENEMY_SPOTTED'
};

// Step 2: Create your event system and game objects
const eventEmitter = new EventEmitter();
const hero = createHero(0, 0);

Τι κάνει αυτός ο κώδικας:

  • Ορίζει ένα αντικείμενο σταθερών για να αποτρέψει λάθη στα ονόματα μηνυμάτων
  • Δημιουργεί μια instance του event emitter για να χειριστεί όλη την επικοινωνία
  • Αρχικοποιεί έναν ήρωα στη θέση εκκίνησης
// Step 3: Set up event listeners (subscribers)
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
  hero.moveTo(hero.x - 5, hero.y);
  console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
});

eventEmitter.on(Messages.HERO_MOVE_RIGHT, () => {
  hero.moveTo(hero.x + 5, hero.y);
  console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
});

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

  • Καταχωρήσει listeners γεγονότων που ανταποκρίνονται σε μηνύματα κίνησης
  • Ενημερώσει τη θέση του ήρωα με βάση την κατεύθυνση κίνησης
  • Προσθέσει καταγραφή κονσόλας για να παρακολουθεί τις αλλαγές θέσης του ήρωα
  • Διαχωρίσει τη λογική κίνησης από τη διαχείριση εισόδου
// Step 4: Connect keyboard input to events (publishers)
window.addEventListener('keydown', (event) => {
  switch(event.key) {
    case 'ArrowLeft':
      eventEmitter.emit(Messages.HERO_MOVE_LEFT);
      break;
    case 'ArrowRight':
      eventEmitter.emit(Messages.HERO_MOVE_RIGHT);
      break;
  }
});

Κατανοώντας αυτές τις έννοιες:

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

💡 Χρήσιμη Συμβουλή: Η ομορφιά αυτού του μοτίβου είναι η ευελιξία! Μπορείτε εύκολα να προσθέσετε ηχητικά εφέ, δονήσεις οθόνης ή εφέ σωματιδίων απλά προσθέτοντας περισσότερους listeners γεγονότων χωρίς να χρειάζεται να τροποποιήσετε τον υπάρχοντα κώδικα πληκτρολογίου ή κίνησης.

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

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

Γιατί το Pub/Sub Κλιμακώνεται Αποτελεσματικά

Το μοτίβο pub/sub διατηρεί την απλότητα καθώς οι εφαρμογές γίνονται πιο περίπλοκες. Είτε διαχειρίζεστε δεκάδες εχθρούς, δυναμικές ενημερώσεις UI ή συστήματα ήχου, το μοτίβο χειρίζεται την αυξημένη κλίμακα χωρίς αλλαγές στην αρχιτεκτονική. Νέα χαρακτηριστικά ενσωματώνονται στο υπάρχον σύστημα γεγονότων χωρίς να επηρεάζουν τη λειτουργικότητα που έχει ήδη καθιερωθεί.

⚠️ Συνηθισμένο Λάθος: Μην δημιουργείτε πάρα πολλούς συγκεκριμένους τύπους μηνυμάτων από νωρίς. Ξεκινήστε με ευρείες κατηγορίες και βελτιώστε τις καθώς οι ανάγκες του παιχνιδιού σας γίνονται πιο σαφείς.

Καλές πρακτικές που πρέπει να ακολουθήσετε:

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

Πρόκληση GitHub Copilot Agent 🚀

Χρησιμοποιήστε τη λειτουργία Agent για να ολοκληρώσετε την παρακάτω πρόκληση:

Περιγραφή: Δημιουργήστε ένα απλό σύστημα αντικειμένων παιχνιδιού χρησιμοποιώντας τόσο την κληρονομικότητα όσο και το μοτίβο pub/sub. Θα υλοποιήσετε ένα βασικό παιχνίδι όπου διαφορετικά αντικείμενα μπορούν να επικοινωνούν μέσω γεγονότων χωρίς να γνωρίζουν άμεσα το ένα το άλλο.

Προτροπή: Δημιουργήστε ένα σύστημα παιχνιδιού JavaScript με τις εξής απαιτήσεις: 1) Δημιουργήστε μια βασική κλάση GameObject με συντεταγμένες x, y και μια ιδιότητα τύπου. 2) Δημιουργήστε μια κλάση Hero που επεκτείνει το GameObject και μπορεί να κινείται. 3) Δημιουργήστε μια κλάση Enemy που επεκτείνει το GameObject και μπορεί να κυνηγά τον ήρωα. 4) Υλοποιήστε μια κλάση EventEmitter για το μοτίβο pub/sub. 5) Ρυθμίστε listeners γεγονότων ώστε όταν ο ήρωας κινείται, οι κοντινοί εχθροί να λαμβάνουν ένα γεγονός 'HERO_MOVED' και να ενημερώνουν τη θέση τους για να κινηθούν προς τον ήρωα. Συμπεριλάβετε δηλώσεις console.log για να δείξετε την επικοινωνία μεταξύ των αντικειμένων.

Μάθετε περισσότερα για τη λειτουργία agent εδώ.

🚀 Πρόκληση

Σκεφτείτε πώς το μοτίβο pub-sub μπορεί να βελτιώσει την αρχιτεκτονική του παιχνιδιού. Προσδιορίστε ποια εξαρτήματα πρέπει να εκπέμπουν γεγονότα και πώς το σύστημα πρέπει να ανταποκρίνεται. Σχεδιάστε μια ιδέα παιχνιδιού και χαρτογραφήστε τα μοτίβα επικοινωνίας μεταξύ των εξαρτημάτων του.

Ερωτηματολόγιο μετά τη διάλεξη

Ερωτηματολόγιο μετά τη διάλεξη

Ανασκόπηση & Αυτομελέτη

Μάθετε περισσότερα για το Pub/Sub διαβάζοντας σχετικά.

Εργασία

Σχεδιάστε ένα παιχνίδι


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