Πίνακας περιεχομένων:

Παιχνίδι Platformer με ελεγχόμενο Arduino με Joystick και δέκτη IR: 3 βήματα (με εικόνες)
Παιχνίδι Platformer με ελεγχόμενο Arduino με Joystick και δέκτη IR: 3 βήματα (με εικόνες)

Βίντεο: Παιχνίδι Platformer με ελεγχόμενο Arduino με Joystick και δέκτη IR: 3 βήματα (με εικόνες)

Βίντεο: Παιχνίδι Platformer με ελεγχόμενο Arduino με Joystick και δέκτη IR: 3 βήματα (με εικόνες)
Βίντεο: ΠΩΣ ΝΑ ΠΟΥΜΕ ΠΛΑΤΦΟΡΜΕΡ; (HOW TO SAY PLATFORMERS?) 2024, Ιούλιος
Anonim
Παιχνίδι Platformer με ελεγχόμενο Arduino με Joystick και δέκτη IR
Παιχνίδι Platformer με ελεγχόμενο Arduino με Joystick και δέκτη IR

Σήμερα, θα χρησιμοποιήσουμε έναν μικροελεγκτή Arduino για να ελέγξουμε ένα απλό παιχνίδι platformer με βάση την C#. Χρησιμοποιώ το Arduino για να λάβω είσοδο από μια μονάδα joystick και να στείλω αυτήν την είσοδο στην εφαρμογή C# που ακούει και αποκωδικοποιεί την είσοδο μέσω σειριακής σύνδεσης. Παρόλο που δεν χρειάζεστε προηγούμενη εμπειρία στη δημιουργία βιντεοπαιχνιδιών για να ολοκληρώσετε το έργο, μπορεί να χρειαστεί λίγος χρόνος για να απορροφήσετε μερικά από τα πράγματα που συμβαίνουν στον "βρόχο παιχνιδιών", για το οποίο θα συζητήσουμε αργότερα.

Για να ολοκληρώσετε αυτό το έργο, θα χρειαστείτε:

  • Κοινότητα Visual Studio
  • Ένα Arduino Uno (ή παρόμοιο)
  • Μονάδα χειριστηρίου χειριστηρίου
  • Υπομονή

Αν είστε έτοιμοι να ξεκινήσετε, συνεχίστε!

Βήμα 1: Συνδέστε το Joystick και το IR LED

Συνδέστε το Joystick και το IR LED
Συνδέστε το Joystick και το IR LED
Συνδέστε το Joystick και το IR LED
Συνδέστε το Joystick και το IR LED

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

Οι ακίδες που χρησιμοποιούνται στη ρύθμιση είναι:

  • A0 (αναλογικό) <- Οριζόντιος ή άξονας Χ
  • A1 (αναλογικό) <- Κάθετος ή άξονας Υ
  • Καρφίτσα 2 <- Είσοδος διακόπτη Joystick
  • Καρφίτσα 2 <- Είσοδος LED υπέρυθρης ακτινοβολίας
  • VCC <- 5V
  • Εδαφος
  • Έδαφος #2

Βήμα 2: Δημιουργήστε ένα νέο σκίτσο

Δημιουργήστε ένα νέο σκίτσο
Δημιουργήστε ένα νέο σκίτσο

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

Έτσι, δημιουργήστε πρώτα ένα νέο σκίτσο στο πρόγραμμα επεξεργασίας κώδικα Arduino. Θα δείξω τον κωδικό μου και μετά θα εξηγήσω τι κάνει:

#include "IRremote.h"

// μεταβλητές IR int δέκτης = 3; // Pin σήματος του δέκτη IR IRrecv irrecv (δέκτης). // δημιουργία στιγμιότυπων αποτελεσμάτων αποκρυπτογράφησης 'irrecv'. // δημιουργία στιγμιότυπου «αποκωδικοποιήσεων_ αποτελεσμάτων» // Μεταβλητές χειριστηρίου/παιχνιδιού int xPos = 507; int yPos = 507; byte joyXPin = A0; byte joyYPin = A1; byte joySwitch = 2; πτητικό byte clickCounter = -1; int minMoveHigh = 530; int minMoveLow = 490; int currentSpeed = 550; // Προεπιλογή = μια μέση ταχύτητα int speedIncrement = 25; // Ποσό για την αύξηση/μείωση της ταχύτητας με την είσοδο Y ανυπόγραφο μεγάλο ρεύμα = 0; // Διατηρεί την τρέχουσα χρονική σήμανση int wait = 40; // ms για αναμονή μεταξύ των μηνυμάτων [Σημείωση: χαμηλότερη αναμονή = γρηγορότερο ρυθμό καρέ] πτητικό κουμπί boolPressed = false; // Μετρήστε αν πατηθεί το κουμπί void setup () {Serial.begin (9600); pinMode (joySwitch, INPUT_PULLUP); attachInterrupt (0, άλμα, ΠΤΩΣΗ)? ρεύμα = millis (); // Ρύθμιση της τρέχουσας ώρας // Ρύθμιση δέκτη υπέρυθρων ακτίνων: irrecv.enableIRIn (); // Εκκίνηση του δέκτη} // setup void loop () {int xMovement = analogRead (joyXPin); int yPos = analogRead (joyYPin); // Χειριστείτε την κίνηση του Joystick X ανεξάρτητα από το χρόνο: εάν (xMovement> minMoveHigh || xMovement current + wait) {currentSpeed = yPos> minMoveLow && yPos <minMoveHigh // Αν μετακινηθείτε μόνο λίγο…; currentSpeed //… απλά επιστρέψτε την τρέχουσα ταχύτητα: getSpeed (yPos); // Αλλάξτε yPos μόνο εάν το joystick μετακινήθηκε σημαντικά // int απόσταση =; Serial.print ((String) xPos + "," + (String) yPos + ',' + (String) currentSpeed + '\ n'); ρεύμα = millis (); }} // loop int getSpeed (int yPos) {// Οι αρνητικές τιμές δείχνουν ότι το Joystick μετακινήθηκε αν (yPos 1023? 1023: currentSpeed + speedIncrement;} else if (yPos> minMoveHigh) // Ερμηνεύεται "Κάτω" {// Προστασία από περνάει κάτω από 0 απομακρυσμένο, χειριστείτε τη σωστή απόκριση void translateIR (decode_results results) // αναλαμβάνει δράση με βάση τον κωδικό IR που έχει ληφθεί {switch (results.value) {case 0xFF18E7: //Serial.println("2 "); currentSpeed += speedIncrement * 2; θραύση; θήκη 0xFF10EF: //Serial.println("4 "); xPos = -900; θραύση; θήκη 0xFF38C7: //Serial.println("5"); jump (); break; περίπτωση 0xFF5AA5: // Σειριακό println ("6"); xPos = 900; διάλειμμα; θήκη 0xFF4AB5: //Serial.println("8 "); currentSpeed -= speedIncrement * 2; διακοπή; προεπιλογή: //Serial.println (" άλλο κουμπί "); break;} // Διακόπτης τερματισμού} // ΤΕΛΟΣ translateIR

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

int minYMoveUp = 520;

int minYMoveDown = 500;

Όταν εκτελείται το πρόγραμμα, η αναλογική είσοδος από το joystick τείνει να πηδάει γύρω, συνήθως παραμένει περίπου στα 507. Για να διορθωθεί αυτό, η είσοδος δεν αλλάζει εκτός αν είναι μεγαλύτερη από minYMoveUp ή μικρότερη από minYMoveDown.

pinMode (joySwitch, INPUT_PULLUP);

attachInterrupt (0, άλμα, ΠΤΩΣΗ)?

Η μέθοδος attachInterrupt () μας επιτρέπει να διακόπτουμε τον κανονικό βρόχο ανά πάσα στιγμή, έτσι ώστε να μπορούμε να λαμβάνουμε είσοδο, όπως το πάτημα του κουμπιού όταν γίνεται κλικ στο κουμπί του joystick. Εδώ, έχουμε επισυνάψει τη διακοπή στη γραμμή πριν από αυτήν, χρησιμοποιώντας τη μέθοδο pinMode (). Μια σημαντική σημείωση εδώ είναι ότι για να επισυνάψετε μια διακοπή στο Arduino Uno, πρέπει να χρησιμοποιήσετε είτε το pin 2 είτε το 3. Τα άλλα μοντέλα χρησιμοποιούν διαφορετικούς ακροδέκτες διακοπής, οπότε ίσως χρειαστεί να ελέγξετε ποιες καρφίτσες χρησιμοποιεί το μοντέλο σας στον ιστότοπο του Arduino. Η δεύτερη παράμετρος αφορά τη μέθοδο επανάκλησης, εδώ ονομάζεται ISR ή "Διακοπή ρουτίνας υπηρεσίας". Δεν πρέπει να λαμβάνει παραμέτρους ή να επιστρέφει τίποτα.

Serial.print (…)

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

Εάν είστε έτοιμοι να δοκιμάσετε τον κωδικό σας, ανεβάστε τον στο Arduino και πατήστε [Shift] + [Ctrl] + [M] για να ανοίξετε τη σειριακή οθόνη και να δείτε αν λαμβάνετε έξοδο. Εάν λαμβάνετε δεδομένα από το Arduino, είμαστε έτοιμοι να προχωρήσουμε στο τμήμα C# του κώδικα…

Βήμα 3: Δημιουργήστε το έργο C#

Για να εμφανίσουμε τα γραφικά μας, ξεκίνησα αρχικά ένα έργο στην Επεξεργασία, αλλά αργότερα αποφάσισα ότι θα ήταν πολύ αργό να εμφανιστούν όλα τα αντικείμενα που πρέπει να εμφανίσουμε. Έτσι, επέλεξα να χρησιμοποιήσω το C#, το οποίο αποδείχθηκε πολύ πιο ομαλό και ανταποκρινόμενο κατά τον χειρισμό των δεδομένων μας.

Για το μέρος C# του έργου, είναι καλύτερο να κατεβάσετε απλά το αρχείο.zip και να το εξαγάγετε στο δικό του φάκελο και μετά να το τροποποιήσετε. Υπάρχουν δύο φάκελοι στο αρχείο zip. Για να ανοίξετε το έργο στο Visual Studio, εισαγάγετε το φάκελο RunnerGame_CSharp στην Εξερεύνηση των Windows. Εδώ, κάντε διπλό κλικ στο αρχείο.sln (λύση) και το VS θα φορτώσει το έργο.

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

Η κατηγορία κουτιού

Δημιούργησα την κλάση box για να σας επιτρέψω να δημιουργήσετε απλά ορθογώνια αντικείμενα που μπορούν να σχεδιαστούν στην οθόνη σε μορφή παραθύρων. Η ιδέα είναι να δημιουργηθεί μια κλάση που μπορεί να επεκταθεί χρησιμοποιώντας άλλες κλάσεις που μπορεί να θέλουν να σχεδιάσουν κάποιο είδος γραφικών. Η λέξη -κλειδί "εικονική" χρησιμοποιείται έτσι ώστε άλλες κλάσεις να τις παρακάμψουν (χρησιμοποιώντας τη λέξη -κλειδί "παράκαμψη"). Με αυτόν τον τρόπο, μπορούμε να έχουμε την ίδια συμπεριφορά για την κλάση Player και την κλάση Πλατφόρμα όταν χρειαστεί, και επίσης να τροποποιήσουμε τα αντικείμενα όπως χρειαζόμαστε.

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

Ωστόσο, θα εκθέσω κάποια από τη λογική της τάξης μου "Box":

δημόσιο εικονικό bool IsCollidedX (Box otherObject) {…}

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

δημόσιο εικονικό bool IsCollidedY (Box otherObject) {…}

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

δημόσιο εικονικό bool IsCollided (Πλαίσιο otherObject) {…}

Αυτό συνδυάζει τις συγκρούσεις Χ και Υ, επιστρέφοντας αν συγκρουστεί κάποιο αντικείμενο με αυτό.

δημόσιο εικονικό κενό OnPaint (γραφικά γραφικών) {…}

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

Η κατηγορία χαρακτήρων

Η κατηγορία Χαρακτήρας επεκτείνει την τάξη μου, οπότε έχουμε ορισμένες φυσικές δυνατότητες. Δημιούργησα τη μέθοδο "CheckForCollisions" για να ελέγξω γρήγορα όλες τις πλατφόρμες που έχουμε δημιουργήσει για σύγκρουση. Η μέθοδος "Μετάβαση" ορίζει την ανοδική ταχύτητα του παίκτη στη μεταβλητή JumpSpeed, η οποία στη συνέχεια τροποποιείται καρέ-καρέ στην κλάση MainWindow.

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

Η τάξη πλατφόρμας

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

Η κλάση MainWindow

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

foreach (θύρα συμβολοσειράς στο SerialPort. GetPortNames ())

Console. WriteLine ("ΔΙΑΘΕΣΙΜΕΣ ΘΥΡΕΣ:" + θύρα);

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

SerialPort = νέο SerialPort (SerialPort. GetPortNames () [2], 9600, Parity. None, 8, StopBits. One);

Δώστε μεγάλη προσοχή στην εντολή: SerialPort. GetPortNames () [2]. Το [2] δηλώνει ποια σειριακή θύρα θα χρησιμοποιηθεί. Για παράδειγμα, εάν το πρόγραμμα εκτύπωνε "COM1, COM2, COM3", θα ακούγαμε στο COM3 επειδή η αρίθμηση ξεκινά στο 0 του πίνακα.

Επίσης στον κατασκευαστή, δημιουργούμε όλες τις πλατφόρμες με ημι-τυχαία απόσταση και τοποθέτηση στην κατεύθυνση Υ στην οθόνη. Όλες οι πλατφόρμες προστίθενται σε ένα αντικείμενο λίστας, το οποίο στο C# είναι απλώς ένας πολύ φιλικός προς τον χρήστη και αποτελεσματικός τρόπος διαχείρισης μιας δομής δεδομένων που μοιάζει με πίνακα. Στη συνέχεια, δημιουργούμε το Player, το οποίο είναι το αντικείμενο Character, ορίζουμε το σκορ στο 0 και το GameOver σε false.

ιδιωτικό στατικό κενό DataReceived (αντικείμενο αποστολέα, SerialDataReceivedEventArgs e)

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

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

Συνιστάται: