Mục lục:

Trò chơi Platformer điều khiển bằng Arduino với Cần điều khiển và Bộ thu IR: 3 bước (có Hình ảnh)
Trò chơi Platformer điều khiển bằng Arduino với Cần điều khiển và Bộ thu IR: 3 bước (có Hình ảnh)

Video: Trò chơi Platformer điều khiển bằng Arduino với Cần điều khiển và Bộ thu IR: 3 bước (có Hình ảnh)

Video: Trò chơi Platformer điều khiển bằng Arduino với Cần điều khiển và Bộ thu IR: 3 bước (có Hình ảnh)
Video: 🔴 Arduino #10 | Remote Hồng Ngoại Kết Hợp Mắt Thu Hồng Ngoại Bật Tắt Thiết Bị Từ Xa 2024, Tháng mười một
Anonim
Trò chơi Platformer điều khiển bằng Arduino với Cần điều khiển và Bộ thu IR
Trò chơi Platformer điều khiển bằng Arduino với Cần điều khiển và Bộ thu IR

Hôm nay, chúng ta sẽ sử dụng vi điều khiển Arduino để điều khiển một trò chơi platformer đơn giản dựa trên C #. Tôi đang sử dụng Arduino để lấy đầu vào từ mô-đun cần điều khiển và gửi đầu vào đó đến ứng dụng C #, ứng dụng này sẽ lắng nghe và giải mã đầu vào qua kết nối Serial. Mặc dù bạn không cần bất kỳ kinh nghiệm xây dựng trò chơi điện tử nào trước đó để hoàn thành dự án, nhưng có thể cần một khoảng thời gian để hấp thụ một số điều đang diễn ra trong "vòng lặp trò chơi" mà chúng ta sẽ thảo luận sau.

Để hoàn thành dự án này, bạn sẽ cần:

  • Cộng đồng Visual Studio
  • Arduino Uno (hoặc tương tự)
  • Mô-đun điều khiển cần điều khiển
  • Kiên nhẫn

Nếu bạn đã sẵn sàng để bắt đầu, hãy tiếp tục!

Bước 1: Kết nối cần điều khiển và đèn LED hồng ngoại

Kết nối cần điều khiển và đèn LED hồng ngoại
Kết nối cần điều khiển và đèn LED hồng ngoại
Kết nối cần điều khiển và đèn LED hồng ngoại
Kết nối cần điều khiển và đèn LED hồng ngoại

Ở đây, hookup khá đơn giản. Tôi đã bao gồm các sơ đồ chỉ hiển thị cần điều khiển được nối, cũng như thiết lập tôi đang sử dụng, bao gồm cần điều khiển cộng với đèn LED hồng ngoại để điều khiển trò chơi bằng điều khiển từ xa, đi kèm với nhiều bộ Arduino. Đây là tùy chọn, nhưng có vẻ là một ý tưởng tuyệt vời để có thể chơi game không dây.

Các chân được sử dụng trong thiết lập là:

  • A0 (tương tự) <- Trục ngang hoặc trục X
  • A1 (analog) <- Trục dọc hoặc trục Y
  • Pin 2 <- Đầu vào Joystick Switch
  • Chân 2 <- Đầu vào LED hồng ngoại
  • VCC <- 5V
  • Đất
  • Căn hộ số 2

Bước 2: Tạo một bản phác thảo mới

Tạo một bản phác thảo mới
Tạo một bản phác thảo mới

Chúng tôi sẽ bắt đầu với việc tạo tệp phác thảo Arduino của chúng tôi. Thao tác này sẽ thăm dò cần điều khiển để tìm các thay đổi và gửi những thay đổi đó đến chương trình C # sau mỗi vài mili giây. Trong một trò chơi điện tử thực tế, chúng tôi sẽ kiểm tra cổng nối tiếp trong một vòng lặp trò chơi để tìm đầu vào, nhưng tôi đã bắt đầu trò chơi như một thử nghiệm, vì vậy tốc độ khung hình thực sự dựa trên số lượng sự kiện trên cổng nối tiếp. Tôi đã thực sự bắt đầu dự án trong dự án chị em Arduino, Xử lý, nhưng hóa ra nó chậm hơn rất nhiều và không thể xử lý số lượng ô trên màn hình.

Vì vậy, trước tiên hãy tạo một Sketch mới trong chương trình biên tập mã Arduino. Tôi sẽ hiển thị mã của mình và sau đó giải thích những gì nó làm:

#include "IRremote.h"

// Các biến IR int receiver = 3; // Chân Tín hiệu của IR receiver IRrecv’tcv (receiver); // tạo phiên bản của kết quả decode_results 'không thể giải mã'; // tạo phiên bản 'decode_results' // Các biến Joystick / game int xPos = 507; int yPos = 507; byte joyXPin = A0; byte joyYPin = A1; byte joySwitch = 2; byte dễ bay hơi clickCounter = -1; int minMoveHigh = 530; int minMoveLow = 490; int currentSpeed = 550; // Mặc định = một tốc độ trung bình int speedIncrement = 25; // Lượng tăng / giảm tốc độ với đầu vào Y unsigned long current = 0; // Giữ dấu thời gian hiện tại int wait = 40; // ms để đợi giữa các thông báo [Lưu ý: low wait = speed framerate] dễ bay hơi bool buttonPressed = false; // Đo nếu nút được nhấn void setup () {Serial.begin (9600); pinMode (joySwitch, INPUT_PULLUP); mountInterrupt (0, nhảy, FALLING); hiện tại = millis (); // Thiết lập thời gian hiện tại // Thiết lập bộ thu hồng ngoại:’tcv.enableIRIn (); // Khởi động đầu thu} // thiết lập void loop () {int xMovement = analogRead (joyXPin); int yPos = analogRead (joyYPin); // Xử lý chuyển động của Cần điều khiển X bất kể thời gian: if (xMovement> minMoveHigh || xMovement current + wait) {currentSpeed = yPos> minMoveLow && yPos <minMoveHigh // Nếu chỉ di chuyển một chút…? currentSpeed //… chỉ cần trả lại tốc độ hiện tại: getSpeed (yPos); // Chỉ thay đổi yPos nếu cần điều khiển di chuyển đáng kể // int distance =; Serial.print ((Chuỗi) xPos + "," + (Chuỗi) yPos + ',' + (Chuỗi) currentSpeed + '\ n'); hiện tại = millis (); }} // vòng lặp int getSpeed (int yPos) {// Giá trị âm cho biết Cần điều khiển được di chuyển lên if (yPos 1023? 1023: currentSpeed + speedIncrement;} else if (yPos> minMoveHigh) // Đã diễn giải "Xuống" {// Bảo vệ khỏi xuống dưới 0 return currentSpeed - speedIncrement <0? 0: currentSpeed - speedIncrement;}} // getSpeed void jump () {buttonPressed = true; // Nhấn nút cho biết.} // jump // Khi nhấn một nút trên từ xa, xử lý phản hồi thích hợp void translateIR (decode_results results) // thực hiện hành động dựa trên mã IR nhận được {switch (results.value) {case 0xFF18E7: //Serial.println("2 "); currentSpeed + = speedIncrement * 2; break; case 0xFF10EF: //Serial.println("4 "); xPos = -900; break; case 0xFF38C7: //Serial.println("5"); jump (); break; case 0xFF5AA5: // Nối tiếp. println ("6"); xPos = 900; break; case 0xFF4AB5: //Serial.println("8 "); currentSpeed - = speedIncrement * 2; break; default: //Serial.println (" nút khác "); break;} // Kết thúc chuyển đổi} // KẾT THÚC translateIR

Tôi đã cố gắng tạo mã để gần như tự giải thích, nhưng có một số điều đáng nói. Một điều tôi đã cố gắng giải thích là trong những dòng sau:

int minYMoveUp = 520;

int minYMoveDown = 500;

Khi chương trình đang chạy, đầu vào tương tự từ cần điều khiển có xu hướng nhảy xung quanh, thường ở khoảng 507. Để khắc phục điều này, đầu vào không thay đổi trừ khi lớn hơn minYMoveUp hoặc nhỏ hơn minYMoveDown.

pinMode (joySwitch, INPUT_PULLUP);

mountInterrupt (0, nhảy, FALLING);

Phương thức attachmentInterrupt () cho phép chúng ta ngắt vòng lặp bình thường bất kỳ lúc nào, để chúng ta có thể nhận đầu vào, giống như thao tác nhấn nút khi nhấp vào nút cần điều khiển. Ở đây, chúng tôi đã đính kèm ngắt trong dòng trước nó, sử dụng phương thức pinMode (). Một lưu ý quan trọng ở đây là để gắn một ngắt trên Arduino Uno, bạn phải sử dụng chân 2 hoặc 3. Các mẫu khác sử dụng các chân ngắt khác nhau, vì vậy bạn có thể phải kiểm tra xem mô hình của mình sử dụng chân nào trên trang web Arduino. Tham số thứ hai dành cho phương thức gọi lại, ở đây được gọi là ISR hoặc "Quy trình dịch vụ ngắt". Nó không nên nhận bất kỳ tham số hoặc trả về bất kỳ thứ gì.

Serial.print (…)

Đây là dòng sẽ gửi dữ liệu của chúng tôi đến trò chơi C #. Ở đây, chúng tôi gửi số đọc trục X, đọc trục Y và một biến tốc độ cho trò chơi. Các bài đọc này có thể được mở rộng để bao gồm các đầu vào và bài đọc khác để làm cho trò chơi thú vị hơn, nhưng ở đây, chúng tôi sẽ chỉ sử dụng một vài.

Nếu bạn đã sẵn sàng kiểm tra mã của mình, hãy tải nó lên Arduino và nhấn [Shift] + [Ctrl] + [M] để mở màn hình nối tiếp và xem liệu bạn có nhận được bất kỳ đầu ra nào không. Nếu bạn đang nhận dữ liệu từ Arduino, chúng tôi sẵn sàng chuyển sang phần C # của mã…

Bước 3: Tạo Dự án C #

Để hiển thị đồ họa của chúng tôi, ban đầu tôi bắt đầu một dự án trong Xử lý, nhưng sau đó quyết định rằng sẽ quá chậm để hiển thị tất cả các đối tượng mà chúng tôi cần hiển thị. Vì vậy, tôi đã chọn sử dụng C #, điều này hóa ra mượt mà và nhạy hơn nhiều khi xử lý đầu vào của chúng tôi.

Đối với phần C # của dự án, tốt nhất là chỉ cần tải xuống tệp.zip và giải nén nó vào thư mục riêng của nó, sau đó sửa đổi nó. Có hai thư mục trong tệp zip. Để mở dự án trong Visual Studio, hãy nhập thư mục RunnerGame_CSharp trong Windows Explorer. Tại đây, nhấp đúp vào tệp.sln (giải pháp) và VS sẽ tải dự án.

Có một vài lớp khác nhau mà tôi đã tạo cho trò chơi. Tôi sẽ không đi vào tất cả các chi tiết về mỗi lớp, nhưng tôi sẽ cung cấp một cái nhìn tổng quan về những gì các lớp chính dành cho.

Lớp hộp

Tôi đã tạo lớp hộp để cho phép bạn tạo các đối tượng hình chữ nhật đơn giản có thể được vẽ trên màn hình dưới dạng cửa sổ. Ý tưởng là tạo một lớp có thể được mở rộng bằng cách sử dụng các lớp khác có thể muốn vẽ một số loại đồ họa. Từ khóa "virtual" được sử dụng để các lớp khác có thể ghi đè chúng (sử dụng từ khóa "override"). Bằng cách đó, chúng ta có thể nhận được cùng một hành vi đối với lớp Người chơi và lớp Nền tảng khi chúng ta cần, và cũng có thể sửa đổi các đối tượng theo cách chúng ta cần.

Đừng lo lắng quá nhiều về tất cả các thuộc tính và các lệnh gọi. Tôi đã viết lớp học này để tôi có thể mở rộng nó cho bất kỳ trò chơi hoặc chương trình đồ họa nào mà tôi có thể muốn thực hiện trong tương lai. Nếu bạn cần đơn giản vẽ một hình chữ nhật đang bay, bạn không cần phải viết ra một lớp lớn như thế này. Tài liệu C # có các ví dụ tốt về cách thực hiện việc này.

Tuy nhiên, tôi sẽ đưa ra một số logic của lớp "Box" của mình:

công khai bool ảo IsCollidedX (Box otherObject) {…}

Ở đây chúng tôi kiểm tra va chạm với các đối tượng theo hướng X, vì người chơi chỉ cần kiểm tra va chạm theo hướng Y (lên và xuống) nếu anh ta được xếp cùng hàng với nó trên màn hình.

công khai bool ảo IsCollidedY (Box otherObject) {…}

Khi chúng tôi ở trên hoặc dưới đối tượng trò chơi khác, chúng tôi kiểm tra va chạm Y.

công khai ảo bool IsCollided (Box otherObject) {…}

Điều này kết hợp các va chạm X và Y, trả về liệu có bất kỳ vật thể nào bị va chạm với vật thể này hay không.

public virtual void OnPaint (Đồ họa đồ họa) {…}

Sử dụng phương pháp trên, chúng tôi chuyển bất kỳ đối tượng đồ họa nào vào và sử dụng nó khi chương trình đang chạy. Chúng tôi tạo bất kỳ hình chữ nhật nào có thể cần được vẽ. Tuy nhiên, điều này có thể được sử dụng cho nhiều hình ảnh động. Đối với mục đích của chúng tôi, hình chữ nhật sẽ hoạt động tốt cho cả nền tảng và trình phát.

Lớp nhân vật

Lớp Nhân vật mở rộng lớp Hộp của tôi, vì vậy chúng tôi có một số vật lý nhất định. Tôi đã tạo phương thức "CheckForCollisions" để nhanh chóng kiểm tra tất cả các nền tảng mà chúng tôi đã tạo xem có va chạm hay không. Phương thức "Jump" đặt vận tốc đi lên của người chơi thành biến JumpSpeed, sau đó được sửa đổi từng khung hình trong lớp MainWindow.

Ở đây các va chạm được xử lý hơi khác so với trong lớp Box. Trong trò chơi này, tôi đã quyết định rằng nếu nhảy lên trên, chúng ta có thể nhảy qua một nền tảng, nhưng nó sẽ bắt người chơi của chúng ta rơi xuống nếu va chạm với nó.

Lớp nền tảng

Trong trò chơi này, tôi chỉ sử dụng hàm tạo của lớp này lấy tọa độ X làm đầu vào, tính toán tất cả các vị trí X của nền tảng trong lớp MainWindow. Mỗi nền tảng được thiết lập theo một tọa độ Y ngẫu nhiên từ 1/2 màn hình đến 3/4 chiều cao của màn hình. Chiều cao, chiều rộng và màu sắc cũng được tạo ngẫu nhiên.

Lớp MainWindow

Đây là nơi chúng tôi đặt tất cả logic sẽ được sử dụng trong khi trò chơi đang chạy. Đầu tiên, trong hàm tạo, chúng tôi in tất cả các cổng COM có sẵn cho chương trình.

foreach (cổng chuỗi trong SerialPort. GetPortNames ())

Console. WriteLine ("CỔNG CÓ SN:" + cổng);

Chúng tôi chọn cái mà chúng tôi sẽ chấp nhận giao tiếp, theo cổng nào mà Arduino của bạn đang sử dụng:

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

Hãy chú ý đến lệnh: SerialPort. GetPortNames () [2]. [2] biểu thị cổng nối tiếp nào sẽ sử dụng. Ví dụ, nếu chương trình được in ra "COM1, COM2, COM3", chúng tôi sẽ lắng nghe trên COM3 vì việc đánh số bắt đầu từ 0 trong mảng.

Cũng trong hàm tạo, chúng tôi tạo tất cả các nền tảng với khoảng cách và vị trí bán ngẫu nhiên theo hướng Y trên màn hình. Tất cả các nền tảng đều được thêm vào một đối tượng List, đối tượng này trong C # chỉ đơn giản là một cách rất thân thiện và hiệu quả để quản lý cấu trúc dữ liệu dạng mảng. Sau đó, chúng tôi tạo Người chơi, là đối tượng Nhân vật của chúng tôi, đặt điểm số thành 0 và đặt GameOver thành false.

private static void DataReceive (người gửi đối tượng, SerialDataReceiveEventArgs e)

Đây là phương thức được gọi khi dữ liệu được nhận trên cổng nối tiếp. Đây là nơi chúng tôi áp dụng tất cả các vật lý của mình, quyết định có hiển thị trò chơi qua hay không, di chuyển nền tảng, v.v. Nếu bạn đã từng xây dựng một trò chơi, bạn thường có cái được gọi là "vòng lặp trò chơi", được gọi là mỗi lần khung làm mới. Trong trò chơi này, phương thức DataReceive hoạt động như vòng lặp trò chơi, chỉ thao tác vật lý khi dữ liệu được nhận từ bộ điều khiển. Nó có thể hoạt động tốt hơn khi thiết lập Bộ hẹn giờ trong cửa sổ chính và làm mới các đối tượng dựa trên dữ liệu nhận được, nhưng vì đây là một dự án Arduino, tôi muốn tạo một trò chơi thực sự chạy dựa trên dữ liệu đến từ nó.

Tóm lại, thiết lập này cung cấp một cơ sở tốt để mở rộng trò chơi thành một thứ có thể sử dụng được. Mặc dù vật lý không hoàn hảo nhưng nó hoạt động đủ tốt cho mục đích của chúng tôi, đó là sử dụng Arduino cho một thứ mà mọi người đều thích: chơi trò chơi!

Đề xuất: