สารบัญ:

เกม Platformer ที่ควบคุมโดย Arduino พร้อมจอยสติ๊กและตัวรับสัญญาณ IR: 3 ขั้นตอน (พร้อมรูปภาพ)
เกม Platformer ที่ควบคุมโดย Arduino พร้อมจอยสติ๊กและตัวรับสัญญาณ IR: 3 ขั้นตอน (พร้อมรูปภาพ)

วีดีโอ: เกม Platformer ที่ควบคุมโดย Arduino พร้อมจอยสติ๊กและตัวรับสัญญาณ IR: 3 ขั้นตอน (พร้อมรูปภาพ)

วีดีโอ: เกม Platformer ที่ควบคุมโดย Arduino พร้อมจอยสติ๊กและตัวรับสัญญาณ IR: 3 ขั้นตอน (พร้อมรูปภาพ)
วีดีโอ: MiSTer FPGA News & Updates - DonPachi Public, PSX Updates, GunCon 3 and more 2024, พฤศจิกายน
Anonim
เกม Platformer ที่ควบคุมโดย Arduino พร้อมจอยสติ๊กและตัวรับสัญญาณ IR
เกม Platformer ที่ควบคุมโดย Arduino พร้อมจอยสติ๊กและตัวรับสัญญาณ IR

วันนี้ เราจะใช้ไมโครคอนโทรลเลอร์ Arduino เพื่อควบคุมเกมแพลตฟอร์มที่ใช้ C# อย่างง่าย ฉันใช้ Arduino เพื่อรับอินพุตจากโมดูลจอยสติ๊ก และส่งอินพุตนั้นไปยังแอปพลิเคชัน C# ซึ่งรับฟังและถอดรหัสอินพุตผ่านการเชื่อมต่อแบบอนุกรม แม้ว่าคุณไม่จำเป็นต้องมีประสบการณ์ใดๆ มาก่อนในการสร้างวิดีโอเกมเพื่อทำโปรเจ็กต์ให้เสร็จ แต่อาจต้องใช้เวลาสักพักในการซึมซับสิ่งต่างๆ ที่เกิดขึ้นใน "game loop" ซึ่งเราจะพูดถึงในภายหลัง

เพื่อให้โครงการนี้เสร็จสมบูรณ์ คุณจะต้อง:

  • ชุมชน Visual Studio
  • Arduino Uno (หรือคล้ายกัน)
  • โมดูลควบคุมจอยสติ๊ก
  • ความอดทน

ถ้าพร้อมแล้วลุยต่อ!

ขั้นตอนที่ 1: ต่อจอยสติ๊กและ IR LED

ต่อจอยสติ๊กและ IR LED
ต่อจอยสติ๊กและ IR LED
ต่อจอยสติ๊กและ IR LED
ต่อจอยสติ๊กและ 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 กับสิ่งที่ทุกคนชอบ: เล่นเกม!

แนะนำ: