Παίζοντας Rust σε 24 ώρες: εμπειρία προσωπικής ανάπτυξης

Παίζοντας Rust σε 24 ώρες: εμπειρία προσωπικής ανάπτυξης

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

Το Skillbox προτείνει: Πρακτικό μάθημα δύο ετών "Είμαι προγραμματιστής ιστού PRO".

Υπενθύμιση: για όλους τους αναγνώστες του "Habr" - έκπτωση 10 ρούβλια κατά την εγγραφή σε οποιοδήποτε μάθημα Skillbox χρησιμοποιώντας τον κωδικό προσφοράς "Habr".

Γιατί Rust;

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

Γιατί το παιχνίδι και τι είδους παιχνίδι;

Το να φτιάχνεις παιχνίδια είναι διασκεδαστικό! Μακάρι να υπήρχαν περισσότεροι λόγοι, αλλά για έργα «σπίτι» επιλέγω θέματα που δεν σχετίζονται πολύ με την κανονική μου εργασία. Τι παιχνίδι είναι αυτό; Ήθελα να φτιάξω κάτι σαν προσομοιωτή τένις που να συνδυάζει Cities Skylines, Zoo Tycoon, Prison Architect και το ίδιο το τένις. Σε γενικές γραμμές, αποδείχθηκε ότι ήταν ένα παιχνίδι για μια ακαδημία τένις όπου οι άνθρωποι έρχονται να παίξουν.

Τεχνική κατάρτιση

Ήθελα να χρησιμοποιήσω το Rust, αλλά δεν ήξερα ακριβώς πόση βάση θα χρειαζόταν για να ξεκινήσει. Δεν ήθελα να γράψω pixel shaders και να χρησιμοποιήσω drag-n-drop, οπότε έψαχνα για τις πιο ευέλικτες λύσεις.

Βρήκα χρήσιμους πόρους που μοιράζομαι μαζί σας:

Εξερεύνησα αρκετές μηχανές παιχνιδιών Rust, επιλέγοντας τελικά το Piston και το ggez. Τους συνάντησα ενώ εργαζόμουν σε ένα προηγούμενο έργο. Τελικά επέλεξα το ggez γιατί μου φάνηκε πιο κατάλληλο για την υλοποίηση ενός μικρού 2D παιχνιδιού. Η αρθρωτή δομή του Piston είναι πολύ περίπλοκη για έναν αρχάριο προγραμματιστή (ή κάποιον που εργάζεται για πρώτη φορά με τη Rust).

Δομή παιχνιδιού

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

Έχοντας σκεφτεί τα πάντα, έπιασα δουλειά.

Δημιουργία παιχνιδιού

Αρχή: Κύκλοι και αφαιρέσεις

Πήρα ένα παράδειγμα από το ggez και έκανα έναν κύκλο στην οθόνη. Θαυμάσιος! Τώρα μερικές αφαιρέσεις. Σκέφτηκα ότι θα ήταν ωραίο να απομακρυνθώ από την ιδέα ενός αντικειμένου παιχνιδιού. Κάθε αντικείμενο πρέπει να αποδοθεί και να ενημερωθεί όπως αναφέρεται εδώ:

// the game object trait
trait GameObject {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()>;
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()>;
}
 
// a specific game object - Circle
struct Circle {
    position: Point2,
}
 
 impl Circle {
    fn new(position: Point2) -> Circle {
        Circle { position }
    }
}
impl GameObject for Circle {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
        Ok(())
    }
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        let circle =
            graphics::Mesh::new_circle(ctx, graphics::DrawMode::Fill, self.position, 100.0, 2.0)?;
 
         graphics::draw(ctx, &circle, na::Point2::new(0.0, 0.0), 0.0)?;
        Ok(())
    }
}

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

mpl event::EventHandler for MainState {
    fn update(&mut self, context: &mut Context) -> GameResult<()> {
        // Update all objects
        for object in self.objects.iter_mut() {
            object.update(context)?;
        }
 
        Ok(())
    }
 
    fn draw(&mut self, context: &mut Context) -> GameResult<()> {
        graphics::clear(context);
 
        // Draw all objects
        for object in self.objects.iter_mut() {
            object.draw(context)?;
        }
 
        graphics::present(context);
 
        Ok(())
    }
}

Το main.rs είναι απαραίτητο γιατί περιέχει όλες τις γραμμές κώδικα. Πέρασα λίγο χρόνο στον διαχωρισμό των αρχείων και στη βελτιστοποίηση της δομής του καταλόγου. Έτσι φαινόταν μετά από αυτό:
πόροι -> εδώ βρίσκονται όλα τα στοιχεία (εικόνες)
src
- οντότητες
— game_object.rs
— κύκλος.ρσ
— main.rs -> κύριος βρόχος

Άνθρωποι, πατώματα και εικόνες

Το επόμενο βήμα είναι να δημιουργήσετε ένα αντικείμενο παιχνιδιού Person και να φορτώσετε εικόνες. Όλα θα πρέπει να κατασκευαστούν με βάση πλακάκια 32*32.

Παίζοντας Rust σε 24 ώρες: εμπειρία προσωπικής ανάπτυξης

Γήπεδα τέννις

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

Συνολικά έχουμε δύο τέτοια πλακίδια: 1 και 2.

Κάθε τμήμα του γηπέδου αποτελείται από το πλακίδιο 1 ή το πλακίδιο 2. Μπορούν να τοποθετηθούν κανονικά ή να αναστραφούν 180 μοίρες.

Παίζοντας Rust σε 24 ώρες: εμπειρία προσωπικής ανάπτυξης

Βασικός τρόπος κατασκευής (συναρμολόγησης).

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

Αλλά πρέπει ακόμα να θυμόμαστε τι σημαίνουν το 1 και το 2, γι' αυτό πρόσθεσα ένα wireframe για να καταστήσω σαφές ποιο αντικείμενο επιλέχθηκε. Έτσι φαίνεται.

Παίζοντας Rust σε 24 ώρες: εμπειρία προσωπικής ανάπτυξης

Ερωτήσεις αρχιτεκτονικής και ανακατασκευής

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

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

  • Η ύπαρξη μιας οντότητας που αποδίδει και ενημερώνει η ίδια είναι ένα πρόβλημα, επειδή αυτή η οντότητα δεν θα μπορεί να «γνωρίζει» τι υποτίθεται ότι θα αποδίδει - μια εικόνα και ένα wireframe.
  • έλλειψη εργαλείου για την ανταλλαγή ιδιοτήτων και συμπεριφοράς μεταξύ μεμονωμένων οντοτήτων (για παράδειγμα, η ιδιότητα is_build_mode ή η απόδοση συμπεριφοράς). Θα ήταν δυνατή η χρήση κληρονομικότητας, αν και δεν υπάρχει σωστός τρόπος για να το εφαρμόσετε στο Rust. Αυτό που πραγματικά χρειαζόμουν ήταν η διάταξη.
  • χρειαζόταν ένα εργαλείο αλληλεπίδρασης μεταξύ οντοτήτων για την ανάθεση ατόμων στα δικαστήρια.
  • οι ίδιες οι οντότητες ήταν ένα μείγμα δεδομένων και λογικής που γρήγορα ξέφυγε από τον έλεγχο.

Έκανα περισσότερη έρευνα και ανακάλυψα την αρχιτεκτονική ECS - Entity Component System, που χρησιμοποιείται συνήθως σε παιχνίδια. Εδώ είναι τα οφέλη του ECS:

  • Τα δεδομένα διαχωρίζονται από τη λογική.
  • σύνθεση αντί κληρονομιάς?
  • data-centric αρχιτεκτονική.

Το ECS χαρακτηρίζεται από τρεις βασικές έννοιες:

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

Μετά τη μελέτη του, έγινε σαφές ότι το ECS επιλύει τα ακόλουθα προβλήματα:

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

Αυτό συνέβη μετά την εφαρμογή του ECS.

πόροι -> εδώ βρίσκονται όλα τα στοιχεία (εικόνες)
src
- συστατικά
—θέση.ρσ
— πρόσωπο.rs
— tennis_court.rs
— πάτωμα.ρσ
- wireframe.rs
— mouse_tracked.rs
- πόρους
—ποντίκι.ρσ
- συστήματα
— απόδοση.rs
— σταθερές.rs
— utils.rs
— world_factory.rs -> world factory functions
— main.rs -> κύριος βρόχος

Αναθέτουμε ανθρώπους στα δικαστήρια

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

Τι έχω κάνει:

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

Παίζοντας Rust σε 24 ώρες: εμπειρία προσωπικής ανάπτυξης

Ανακεφαλαίωση

Μου άρεσε πολύ να δουλεύω σε αυτό το απλό παιχνίδι. Επιπλέον, χαίρομαι που χρησιμοποίησα το Rust για να το γράψω, γιατί:

  • Η σκουριά σου δίνει αυτό που χρειάζεσαι.
  • έχει εξαιρετική τεκμηρίωση, το Rust είναι αρκετά κομψό.
  • η συνοχή είναι δροσερή.
  • δεν χρειάζεται να καταφύγετε σε κλωνοποίηση, αντιγραφή ή άλλες παρόμοιες ενέργειες, τις οποίες έκανα συχνά στη C++.
  • Οι επιλογές είναι πολύ εύκολες στη χρήση και χειρίζονται πολύ καλά τα σφάλματα.
  • Εάν το έργο ήταν σε θέση να μεταγλωττιστεί, τότε το 99% του χρόνου λειτουργεί, και ακριβώς όπως θα έπρεπε. Νομίζω ότι τα μηνύματα λάθους του μεταγλωττιστή είναι τα καλύτερα που έχω δει.

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

Το Skillbox προτείνει:

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο