สารบัญ:
วีดีโอ: เกม Platformer ที่ควบคุมโดย Arduino พร้อมจอยสติ๊กและตัวรับสัญญาณ IR: 3 ขั้นตอน (พร้อมรูปภาพ)
2024 ผู้เขียน: John Day | [email protected]. แก้ไขล่าสุด: 2024-01-30 13:07
วันนี้ เราจะใช้ไมโครคอนโทรลเลอร์ Arduino เพื่อควบคุมเกมแพลตฟอร์มที่ใช้ C# อย่างง่าย ฉันใช้ Arduino เพื่อรับอินพุตจากโมดูลจอยสติ๊ก และส่งอินพุตนั้นไปยังแอปพลิเคชัน C# ซึ่งรับฟังและถอดรหัสอินพุตผ่านการเชื่อมต่อแบบอนุกรม แม้ว่าคุณไม่จำเป็นต้องมีประสบการณ์ใดๆ มาก่อนในการสร้างวิดีโอเกมเพื่อทำโปรเจ็กต์ให้เสร็จ แต่อาจต้องใช้เวลาสักพักในการซึมซับสิ่งต่างๆ ที่เกิดขึ้นใน "game loop" ซึ่งเราจะพูดถึงในภายหลัง
เพื่อให้โครงการนี้เสร็จสมบูรณ์ คุณจะต้อง:
- ชุมชน Visual Studio
- Arduino Uno (หรือคล้ายกัน)
- โมดูลควบคุมจอยสติ๊ก
- ความอดทน
ถ้าพร้อมแล้วลุยต่อ!
ขั้นตอนที่ 1: ต่อจอยสติ๊กและ IR LED
ที่นี่การเชื่อมต่อค่อนข้างง่าย ฉันได้รวมไดอะแกรมที่แสดงเฉพาะจอยสติ๊กที่ต่ออยู่ เช่นเดียวกับการตั้งค่าที่ฉันใช้ ซึ่งรวมถึงจอยสติ๊กพร้อมไฟ LED อินฟราเรดสำหรับควบคุมเกมด้วยรีโมทคอนโทรล ซึ่งมาพร้อมกับชุดอุปกรณ์ Arduino จำนวนมาก นี่เป็นทางเลือก แต่ดูเหมือนเป็นความคิดที่ดีที่จะเล่นเกมแบบไร้สายได้
พินที่ใช้ในการตั้งค่าคือ:
- A0 (แอนะล็อก) <- แนวนอนหรือแกน X
- A1 (แอนะล็อก) <- แนวตั้งหรือแกน Y
- ขา 2 <- อินพุตสวิตช์จอยสติ๊ก
- ขา 2 <- อินพุท LED อินฟราเรด
- VCC <- 5V
- พื้น
- กราวด์ #2
ขั้นตอนที่ 2: สร้างภาพร่างใหม่
เราจะเริ่มต้นด้วยการสร้างไฟล์ร่าง Arduino ของเรา สิ่งนี้จะสำรวจจอยสติ๊กสำหรับการเปลี่ยนแปลง และส่งการเปลี่ยนแปลงเหล่านั้นไปยังโปรแกรม C# ทุก ๆ มิลลิวินาที ในวิดีโอเกมจริง เราจะตรวจสอบพอร์ตอนุกรมในลูปเกมเพื่อหาอินพุต แต่ฉันเริ่มเกมด้วยการทดลอง ดังนั้นอัตราเฟรมจึงขึ้นอยู่กับจำนวนเหตุการณ์บนพอร์ตอนุกรม จริง ๆ แล้วฉันเริ่มโครงการในโปรเจ็กต์ในเครือ Arduino คือ Processing แต่ปรากฎว่ามันช้ากว่ามากและไม่สามารถจัดการกับจำนวนกล่องบนหน้าจอได้
ก่อนอื่นให้สร้าง Sketch ใหม่ในโปรแกรมแก้ไขโค้ด Arduino ฉันจะแสดงรหัสของฉันแล้วอธิบายว่ามันทำอะไร:
#include "IRremote.h"
// ตัวแปร IR ตัวรับ int = 3; // ขาสัญญาณของตัวรับสัญญาณ IR IRrecv irrecv (ตัวรับ); // สร้างอินสแตนซ์ของผลลัพธ์ decode_results 'irrecv'; // สร้างอินสแตนซ์ของ 'decode_results' // ตัวแปรจอยสติ๊ก/เกม int xPos = 507; int yPos = 507; ไบต์ joyXPin = A0; ไบต์ joyYPin = A1; ไบต์ joySwitch = 2; ไบต์ที่ระเหยได้ clickCounter = -1; int minMoveHigh = 530; int minMoveLow = 490; int ปัจจุบัน ความเร็ว = 550; // ค่าเริ่มต้น = ความเร็วเฉลี่ย int speedIncrement = 25; // จำนวนที่จะเพิ่ม/ลดความเร็วด้วยอินพุต Y ที่ไม่ได้ลงนามยาวปัจจุบัน = 0; // เก็บประทับเวลาปัจจุบัน int wait = 40; // ms เพื่อรอระหว่างข้อความ [หมายเหตุ: การรอที่ต่ำกว่า = อัตราเฟรมที่เร็วขึ้น] ปุ่มบูลที่ระเหยได้ กดปุ่ม = false; // วัดว่ากดปุ่มตั้งค่าเป็นโมฆะ () { Serial.begin (9600); โหมดพิน (joySwitch, INPUT_PULLUP); แนบInterrupt(0, กระโดด, FALLING); ปัจจุบัน = มิลลิวินาที (); // ตั้งเวลาปัจจุบัน // ตั้งค่าเครื่องรับอินฟราเรด: irrecv.enableIRIn(); // เริ่มตัวรับ } // setup void loop () { int xMovement = analogRead (joyXPin); int yPos = analogRead (joyYPin); // จัดการกับการเคลื่อนไหวของ Joystick X โดยไม่คำนึงถึงเวลา: if (xMovement > minMoveHigh || xMovement current + wait) { currentSpeed = yPos > minMoveLow && yPos < minMoveHigh // หากขยับเพียงเล็กน้อย… ? currentSpeed // …แค่คืนค่าความเร็วปัจจุบัน: getSpeed(yPos); // เปลี่ยน yPos เฉพาะเมื่อจอยสติ๊กเคลื่อนที่อย่างมีนัยสำคัญ //int distance =; Serial.print((สตริง) xPos + "," + (สตริง) yPos + ', ' + (สตริง) currentSpeed + '\n'); ปัจจุบัน = มิลลิวินาที (); } } // วน int getSpeed (int yPos) { // ค่าลบแสดงว่าจอยสติ๊กขยับขึ้น if (yPos 1023 ? 1023: currentSpeed + speedIncrement; } else if (yPos > minMoveHigh) // ตีความว่า "ลง" { // ป้องกันจาก ต่ำกว่า 0 คืนค่า currentSpeed - speedIncrement < 0 ? 0: currentSpeed - speedIncrement; } } // getSpeed void jump () { buttonPressed = true; // ระบุว่ามีการกดปุ่ม } // jump // เมื่อกดปุ่มบน ระยะไกล จัดการกับการตอบสนองที่เหมาะสม void translateIR (decode_results results) // ดำเนินการตามรหัส IR ที่ได้รับ { สวิตช์ (results.value) { กรณี 0xFF18E7: //Serial.println ("2"); currentSpeed += speedIncrement * 2; ตัวแบ่ง กรณี 0xFF10EF: //Serial.println("4"); xPos = -900 ตัวแบ่ง กรณี 0xFF38C7: //Serial.println("5"); jump(); break; case 0xFF5AA5: //Serial. println("6"); xPos = 900; break; case 0xFF4AB5: //Serial.println("8"); currentSpeed -= speedIncrement * 2; break; default: //Serial.println(" other button "); break; }// End switch } //END translateIR
ฉันพยายามสร้างโค้ดเพื่อให้เข้าใจตัวเองเป็นส่วนใหญ่ แต่มีบางสิ่งที่ควรค่าแก่การกล่าวถึง สิ่งหนึ่งที่ฉันพยายามอธิบายคือในบรรทัดต่อไปนี้:
int minYMoveUp = 520;
int minYMoveDown = 500;
เมื่อโปรแกรมกำลังทำงาน อินพุตแบบอะนาล็อกจากจอยสติ๊กมักจะกระโดดไปมา โดยปกติจะอยู่ที่ประมาณ 507 เพื่อแก้ไขปัญหานี้ อินพุตจะไม่เปลี่ยนแปลงเว้นแต่จะใหญ่กว่า minYMoveUp หรือเล็กกว่า minYMoveDown
โหมดพิน (joySwitch, INPUT_PULLUP);
แนบInterrupt(0, กระโดด, FALLING);
วิธี AttachInterrupt() ทำให้เราสามารถขัดจังหวะการวนซ้ำปกติได้ทุกเมื่อ เพื่อให้เราสามารถป้อนข้อมูลได้ เช่น การกดปุ่มเมื่อมีการคลิกปุ่มจอยสติ๊ก ที่นี่ เราได้แนบอินเตอร์รัปต์ในบรรทัดก่อนหน้าโดยใช้เมธอด pinMode() หมายเหตุสำคัญที่นี่คือในการแนบอินเทอร์รัปต์บน Arduino Uno คุณต้องใช้พิน 2 หรือ 3 รุ่นอื่นๆ ใช้พินอินเทอร์รัปต์ที่แตกต่างกัน ดังนั้นคุณอาจต้องตรวจสอบว่าพินใดที่โมเดลของคุณใช้บนเว็บไซต์ Arduino พารามิเตอร์ที่สองใช้สำหรับวิธีการเรียกกลับ ซึ่งเรียกว่า ISR หรือ "กิจวัตรการบริการขัดจังหวะ" ไม่ควรใช้พารามิเตอร์ใด ๆ หรือส่งคืนสิ่งใด
Serial.print(…)
นี่คือบรรทัดที่จะส่งข้อมูลของเราไปยังเกม C# ที่นี่ เราส่งการอ่านแกน X การอ่านแกน Y และตัวแปรความเร็วไปยังเกม การอ่านเหล่านี้สามารถขยายเพื่อรวมอินพุตและการอ่านอื่นๆ เพื่อทำให้เกมน่าสนใจยิ่งขึ้น แต่ในที่นี้ เราจะใช้เพียงสองสามอย่างเท่านั้น
หากคุณพร้อมที่จะทดสอบโค้ดของคุณ ให้อัปโหลดไปยัง Arduino แล้วกด [Shift] + [Ctrl] + [M] เพื่อเปิดมอนิเตอร์แบบอนุกรมและดูว่าคุณได้รับเอาต์พุตหรือไม่ หากคุณได้รับข้อมูลจาก Arduino เราก็พร้อมที่จะย้ายไปยังส่วน C# ของโค้ด…
ขั้นตอนที่ 3: สร้าง C# Project
ในการแสดงกราฟิกของเรา ตอนแรกฉันเริ่มโปรเจ็กต์ในการประมวลผล แต่ต่อมาตัดสินใจว่าจะช้าเกินไปที่จะแสดงออบเจกต์ทั้งหมดที่เราต้องแสดง ดังนั้นฉันจึงเลือกใช้ C# ซึ่งดูราบรื่นและตอบสนองมากขึ้นเมื่อจัดการกับข้อมูลของเรา
สำหรับส่วน C# ของโปรเจ็กต์ วิธีที่ดีที่สุดคือดาวน์โหลดไฟล์.zip และแตกไฟล์ไปยังโฟลเดอร์ของตัวเอง แล้วแก้ไข มีสองโฟลเดอร์ในไฟล์ zip หากต้องการเปิดโครงการใน Visual Studio ให้ป้อนโฟลเดอร์ RunnerGame_CSharp ใน Windows Explorer ที่นี่ ดับเบิลคลิกที่ไฟล์.sln (โซลูชัน) และ VS จะโหลดโปรเจ็กต์
มีคลาสที่แตกต่างกันสองสามคลาสที่ฉันสร้างขึ้นสำหรับเกม ฉันจะไม่ลงรายละเอียดทั้งหมดเกี่ยวกับแต่ละชั้นเรียน แต่ฉันจะให้ภาพรวมว่าคลาสหลักมีไว้เพื่ออะไร
The Box Class
ฉันสร้างคลาสกล่องเพื่อให้คุณสร้างวัตถุสี่เหลี่ยมอย่างง่ายที่สามารถวาดบนหน้าจอในรูปแบบหน้าต่างได้ แนวคิดคือการสร้างคลาสที่สามารถขยายได้โดยใช้คลาสอื่นที่อาจต้องการวาดกราฟิกบางประเภท คีย์เวิร์ด "เสมือน" ถูกใช้เพื่อให้คลาสอื่นสามารถแทนที่ได้ (โดยใช้คีย์เวิร์ด "แทนที่") ด้วยวิธีนี้ เราจะได้รับพฤติกรรมแบบเดียวกันสำหรับคลาส Player และคลาส Platform เมื่อเราต้องการ และแก้ไขออบเจกต์ตามที่เราต้องการ
อย่ากังวลมากเกินไปเกี่ยวกับคุณสมบัติทั้งหมดและโทรออก ฉันเขียนชั้นเรียนนี้เพื่อที่ฉันจะได้ขยายไปสู่เกมหรือโปรแกรมกราฟิกใดๆ ที่ฉันอาจต้องการทำในอนาคต หากคุณต้องการวาดรูปสี่เหลี่ยมผืนผ้าในทันที คุณไม่จำเป็นต้องเขียนคลาสขนาดใหญ่แบบนี้ เอกสาร C# มีตัวอย่างที่ดีเกี่ยวกับวิธีการทำเช่นนี้
อย่างไรก็ตาม ฉันจะวางตรรกะบางอย่างของคลาส "Box" ของฉัน:
บูลเสมือนสาธารณะ IsCollidedX(Box otherObject) { … }
ที่นี่เราตรวจสอบการชนกับวัตถุในทิศทาง X เนื่องจากผู้เล่นจำเป็นต้องตรวจสอบการชนกันในทิศทาง Y (ขึ้นและลง) เท่านั้นหากเขาอยู่ในแนวเดียวกับบนหน้าจอ
บูลเสมือนสาธารณะ IsCollidedY(Box otherObject) { … }
เมื่อเราอยู่เหนือหรือใต้วัตถุเกมอื่น เราจะตรวจสอบการชนกันของ Y
บูลเสมือนสาธารณะ IsCollided(Box otherObject) { … }
ซึ่งรวมการชนกันของ X และ Y เข้าด้วยกัน โดยจะส่งคืนว่ามีวัตถุใดชนกับวัตถุนี้หรือไม่
โมฆะเสมือนสาธารณะ OnPaint (กราฟิกกราฟิก) { … }
โดยใช้วิธีการข้างต้น เราส่งวัตถุกราฟิกใด ๆ เข้าไปและใช้งานในขณะที่โปรแกรมกำลังทำงานอยู่ เราสร้างสี่เหลี่ยมที่อาจจำเป็นต้องวาด สามารถใช้กับแอนิเมชั่นได้หลากหลาย เพื่อจุดประสงค์ของเรา สี่เหลี่ยมจะทำงานได้ดีสำหรับทั้งแพลตฟอร์มและเครื่องเล่น
คลาสตัวละคร
คลาส Character ขยายคลาส Box ของฉัน ดังนั้นเราจึงมีฟิสิกส์บางอย่างที่พร้อมใช้งาน ฉันสร้างวิธี "CheckForCollisions" เพื่อตรวจสอบการชนกันของแพลตฟอร์มทั้งหมดที่เราสร้างขึ้นอย่างรวดเร็ว วิธีการ "กระโดด" จะตั้งค่าความเร็วขึ้นของผู้เล่นเป็นตัวแปร JumpSpeed ซึ่งจากนั้นจะแก้ไขแบบเฟรมต่อเฟรมในคลาส MainWindow
การชนจะได้รับการจัดการที่แตกต่างจากในคลาส Box เล็กน้อย ฉันตัดสินใจในเกมนี้ว่าถ้ากระโดดขึ้นไปเราสามารถกระโดดผ่านแพลตฟอร์มได้ แต่มันจะจับผู้เล่นของเราลงถ้าชนกับมัน
แพลตฟอร์มคลาส
ในเกมนี้ ฉันใช้เฉพาะตัวสร้างของคลาสนี้ซึ่งรับพิกัด X เป็นอินพุต โดยคำนวณตำแหน่ง X ของแพลตฟอร์มทั้งหมดในคลาส MainWindow แต่ละแพลตฟอร์มถูกตั้งค่าที่พิกัด Y แบบสุ่มจาก 1/2 ของหน้าจอถึง 3/4 ของความสูงของหน้าจอ ความสูง ความกว้าง และสีจะถูกสร้างขึ้นแบบสุ่มเช่นกัน
ระดับหน้าต่างหลัก
นี่คือที่ที่เรานำตรรกะทั้งหมดไปใช้ในขณะที่เกมกำลังทำงาน ขั้นแรก ในตัวสร้าง เราพิมพ์พอร์ต 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 ในอาร์เรย์
นอกจากนี้ ในตัวสร้าง เราสร้างแพลตฟอร์มทั้งหมดด้วยการเว้นวรรคและการจัดวางแบบกึ่งสุ่มในทิศทาง Y บนหน้าจอ แพลตฟอร์มทั้งหมดถูกเพิ่มลงในอ็อบเจ็กต์ List ซึ่งในภาษา C# เป็นวิธีที่ใช้งานง่ายและมีประสิทธิภาพมากในการจัดการโครงสร้างข้อมูลแบบอาร์เรย์ จากนั้นเราสร้าง Player ซึ่งเป็นวัตถุตัวละครของเรา ตั้งค่าคะแนนเป็น 0 และตั้งค่า GameOver เป็นเท็จ
โมฆะคงที่ส่วนตัว DataReceived (ผู้ส่งวัตถุ SerialDataReceivedEventArgs e)
นี่คือวิธีการที่เรียกว่าเมื่อได้รับข้อมูลบนพอร์ตอนุกรม นี่คือที่ที่เราใช้ฟิสิกส์ทั้งหมดของเรา ตัดสินใจว่าจะแสดงเกมโอเวอร์ ย้ายแพลตฟอร์ม ฯลฯ หากคุณเคยสร้างเกม คุณมักมีสิ่งที่เรียกว่า "game loop" ซึ่งเรียกว่าทุกครั้งที่เฟรม สดชื่น ในเกมนี้ เมธอด DataReceived ทำหน้าที่เป็น game loop โดยจะจัดการกับฟิสิกส์เมื่อได้รับข้อมูลจากคอนโทรลเลอร์เท่านั้น อาจทำงานได้ดีกว่าในการตั้งค่าตัวจับเวลาในหน้าต่างหลัก และรีเฟรชวัตถุตามข้อมูลที่ได้รับ แต่เนื่องจากเป็นโครงการ Arduino ฉันต้องการสร้างเกมที่รันโดยอิงตามข้อมูลที่มาจากมัน.
โดยสรุป การตั้งค่านี้เป็นพื้นฐานที่ดีสำหรับการขยายเกมไปสู่สิ่งที่ใช้งานได้ แม้ว่าฟิสิกส์จะไม่สมบูรณ์แบบ แต่ก็ทำงานได้ดีพอสำหรับจุดประสงค์ของเรา ซึ่งก็คือการใช้ Arduino กับสิ่งที่ทุกคนชอบ: เล่นเกม!
แนะนำ:
เมทริกซ์ LED ที่ควบคุมโดย Google Assistant !: 7 ขั้นตอน (พร้อมรูปภาพ)
เมทริกซ์ LED ที่ควบคุมโดย Google Assistant !: ในบทช่วยสอนนี้ ฉันจะแสดงให้คุณเห็นถึงวิธีสร้างเมทริกซ์ LED ที่ควบคุมโดย Google Assistant ซึ่งคุณควบคุมรูปแบบได้ทุกที่โดยใช้สมาร์ทโฟน มาเริ่มกันเลย
Adam Savage Pumpkin ที่ควบคุมโดย Alexa: 5 ขั้นตอน (พร้อมรูปภาพ)
Adam Savage Pumpkin ที่ควบคุมโดย Alexa: ไฟทุกดวงในบ้านของฉันฉลาด ดังนั้นฉันจึงค่อนข้างคุ้นเคยกับการตะโกนใส่เพื่อเปิดและปิด แต่มันทำให้ฉันดูโง่เมื่อฉันตะโกนใส่ไฟที่ไม่ใช่ . และฉันก็ดูโง่เป็นพิเศษเวลาตะโกนใส่เทียน ปกติแล้วนี่จะไม่ค่อย
DIY Coffee Roaster ที่ควบคุมโดย Arduino: 13 ขั้นตอน (พร้อมรูปภาพ)
DIY Coffee Roaster ที่ควบคุมโดย Arduino: ในคำแนะนำนี้ เราจะมาดูการปรับเปลี่ยนเครื่องทำป๊อปคอร์นแบบลมร้อนเพื่อเปลี่ยนเป็นเครื่องคั่วกาแฟที่บ้านแบบอัตโนมัติและควบคุมอุณหภูมิ การคั่วกาแฟที่บ้านนั้นง่ายอย่างน่าประหลาดใจ และแม้แต่สิ่งที่พื้นฐานอย่างการทอด
รีโมท IR ที่ควบคุมโดย Amazon Echo: 11 ขั้นตอน (พร้อมรูปภาพ)
รีโมท IR ที่ควบคุมโดย Amazon Echo: ระบบ Amazon Echo สามารถควบคุมหลาย ๆ ด้านของบ้านอัจฉริยะ แต่เต้ารับอัจฉริยะสามารถปิดและเปิดได้เท่านั้น อุปกรณ์จำนวนมากไม่ได้เปิดขึ้นทันทีโดยเสียบปลั๊กง่ายๆ และต้องการขั้นตอนเพิ่มเติม เช่น การกดปุ่มบนรีโมทหรือ
หน้าจอ LCD ที่ควบคุมโดย Android ใน Flowcode7: 8 ขั้นตอน (พร้อมรูปภาพ)
หน้าจอ LCD ที่ควบคุมโดย Android ใน Flowcode7: คำแนะนำนี้ช่วยให้คุณควบคุมหน้าจอ LCD เสมือนที่จำลองโดยใช้ FlowCode7 โดยใช้อุปกรณ์ Android ของคุณ คุณสามารถโยนในแพลตฟอร์มอื่น ๆ ได้ แต่จำเป็นต้องเปิดใช้งาน Bluetooth เราจะใช้ Arduino เป็นอินเทอร์เฟซ