-
-
-
Tổng tiền thanh toán:
-

GAME - ST7565 - Game hứng trứng - phiên bản nâng cấp
07/08/2019

Đây là phiên bản thứ 2 của Game hứng trứng đơn giản, cùng với sự xuất hiện của cô gà mái, hi vọng đây sẽ là sản phẩm Game mang lại nhiều niềm vui trong những ngày đầu năm Đinh Dậu.
1
Là phiên bản nâng cấp >++
Đây là phiên bản đơn giản trước đó:
Làm game hứng trứng đơn giản - phiên bản 1
Còn ở phiên bản thứ 2 này:
Một chú gà đi trên mái kèo, là một nhân vật hư cấu, chú vừa đi vừa đẻ trứng. Để trứng không bị vỡ khi rơi, 2 nhân vật Cả Mẹo và Dần Béo sẽ phải phối hợp nhịp nhàng hứng những quả trứng vào rổ.
Phân vai:
- Cả Mẹo – có cái đầu đinh: cẩm chiếc rổ để hứng trứng.
- Dần Béo – mập ú và rất vụng về là sở đoản của cậu ta: tuy nhiên vì chỉ có một chiếc rỏ , cậu ấy bèn phải dùng chiếc vợt để đỡ những quả trứng đang rơi, trứng sẽ nảy lên khi rơi vào chiếc vợt đó.
Tính Điểm:
- Trứng rơi thẳng vào rổ: cộng 1 điểm.
- Trứng rơi và được Dần Béo đỡ bằng vợt rồi rơi vào rổ: cộng 2 điểm.
- Trứng rơi và được Dần Béo đỡ bằng vợt nhưng không vào rổ: trừ 2 điểm.
- Rơi thẳng xuống đất: Trừ 1 điểm.
Điều khiển:
- Sử dụng 2 nút di chuyển trái phải để di chuyển nhân vật.
- Nhấn nút ”OK” để lựa chọn nhân vật.
- Mũi tên sẽ chỉ vào nhân vật đang được chọn.
2
Phần cứng
Tải về thư viện đồ họa
Bạn hãy tải về thư viện tại bài viết này
Chuẩn bị phần cứng
- Arduino chip atm328 trở lên, mình sử dụng arduino uno r3
- Lcd st7565 128x64 homephone spi
- Nút bấm: 6 nút
Nối mạch
3
Quản lí đối tượng
Có 3 loại đối tượng cần quan tâm:
Đối tượng |
Chức năng, nhiệm vụ |
Thuộc tính |
Gà mái |
Di chuyển qua lại ở phía trên màn hình. Đẻ trứng một cách ngẫu nhiên. |
Tọa độ, hướng. |
Dần và Mẹo. |
Hứng và đỡ trứng đang rơi. |
Tọa độ, hướng nhìn. |
Trứng |
Rơi thẳng từ trên xuống, tọa độ bắt đầu là tọa độ của gà. Bay lên theo đường parabol khi rơi xuống chiếc vợt của Dần Béo. |
Sự tồn tại, tọa độ.
|
Bạn hãy đọc các bài trước về quản lí các đối tượng này nhé !
4
Di chuyển gà
- #include "ST7565_homephone.h"
- // add a bitmap library of a 16x16 fruit icon
- #include "bmps.h"
- ST7565 lcd(3, 4, 5, 6);
- const static unsigned char __attribute__((progmem)) ga_mai_16x16[] = {
- 0x20, 0xD8, 0x2C, 0x0C, 0x9E, 0x72, 0x42, 0xC0, 0x40, 0x40, 0x60, 0xFC, 0xCE, 0x3B, 0x7B, 0x01,
- 0x00, 0x06, 0x09, 0x10, 0x30, 0x48, 0x44, 0x4E, 0x59, 0x71, 0x39, 0x1F, 0x0E, 0x00, 0x00, 0x00
- };
- /* Qủa trứng (6x7) pixel
- * */
- const static unsigned char __attribute__((progmem)) trung_6x7[] = {
- 0x1C, 0x22, 0x41, 0x41, 0x22, 0x1C,
- };
- byte Xmin, Ymin, Xmax, Ymax; //biên
- // mảng cho 2 đối tượng gà(0) và trứng(1)
- byte object[2][4];
- //object[i][0]: tồn tại hay không
- //object[i][1]: tọa độ x
- //object[i][2]: tọa độ y
- //object[i][3]: hướng (1 phải, 2 trên , 3 trái, 4 dưới)
- //
- // Chương trình chạy 1 lần
- void setup()
- {
- lcd.ON();
- lcd.SET(23, 0, 0, 0, 4);
- Xmin = 0;
- Xmax = 125;
- Ymin = 0;
- Ymax = 63;
- }
- void ve_ga_di()
- {
- byte x_cu, y_cu, huong_cu;
- byte x_moi;
- x_cu = object[0][1];
- y_cu = object[0][2];
- huong_cu = object[0][3];
- //lấy thông tin
- if (huong_cu == 1) { //gà quay về bên phải
- x_moi = x_cu + 1;
- lcd.Plus_Bitmap(x_moi, y_cu, 16, 16, ga_mai_16x16, 0, MIRROR, BLACK);
- // xoay bitmap (mirror)
- //
- }
- if (huong_cu == 3) { //gà quay về bên trái
- x_moi = x_cu - 1;
- lcd.Plus_Bitmap(x_moi, y_cu, 16, 16, ga_mai_16x16, 0, NO_MIRROR, BLACK);
- // không xoay (non mirror)
- }
- //ghi lại thông tin
- object[0][1] = x_moi;
- lcd.display();
- } //vẽ gà
- void xoa_ga(byte x, byte y)
- {
- lcd.FillRect(x, y, 16, 16, DELETE); //
- lcd.display();
- } //xóa gà
- void ga_di_va_de_trung()
- { // gà đi và đẻ trứng
- //////////// XỬ LÍ GÀ////////////
- //b1: cấp giấy khai sinh
- if (object[0][0] == 0) {
- // nếu không tồn tại gà thì phải tạo gà
- // cấp thuộc tính cho đối tượng gà
- object[0][1] = Xmin + 5; //hoành độ ban đầu
- object[0][2] = Ymin; // tung độ ban đầu
- object[0][3] = 1; //hướng sang bên phải
- object[0][0] = 1; //cấp quyền tồn tại
- }
- //b2: gà đi và quay đầu
- //lấy thông tin;
- if (object[0][1] <= Xmin) {
- // nếu gà đến biên trái thì phải quay đầu về bên phải
- object[0][3] = 1;
- //ghi lại thông tin
- }
- if (object[0][1] >= Xmax) {
- // nếu gà đến biên phải thì phải quay đầu về bên trái
- object[0][3] = 3;
- //ghi lại thông tin
- }
- ///////////GÀ ĐANG ĐI/////////
- // lấy tt
- xoa_ga(object[0][1], object[0][2]); //xóa ảnh cũ
- ve_ga_di(); // vẽ ảnh mới
- }
- void loop()
- {
- ga_di_va_de_trung();
- delay(100);
- }
Kết quả chạy thử
5
Di chuyển gà và đẻ trứng
- #include "ST7565_homephone.h"
- // add a bitmap library of a 16x16 fruit icon
- #include "bmps.h"
- ST7565 lcd(3, 4, 5, 6);
- const static unsigned char __attribute__((progmem)) ga_mai_16x16[] = {
- 0x20, 0xD8, 0x2C, 0x0C, 0x9E, 0x72, 0x42, 0xC0, 0x40, 0x40, 0x60, 0xFC, 0xCE, 0x3B, 0x7B, 0x01,
- 0x00, 0x06, 0x09, 0x10, 0x30, 0x48, 0x44, 0x4E, 0x59, 0x71, 0x39, 0x1F, 0x0E, 0x00, 0x00, 0x00
- };
- /* Qủa trứng (6x7) pixel
- * */
- const static unsigned char __attribute__((progmem)) trung_6x7[] = {
- 0x1C, 0x22, 0x41, 0x41, 0x22, 0x1C,
- };
- byte Xmin, Ymin, Xmax, Ymax; //biên
- // mảng cho 2 đối tượng 1 gà(0) và 3 trứng(1,2.3)
- byte object[1 + 3][4];
- //object[i][0]: tồn tại hay không
- //object[i][1]: tọa độ x
- //object[i][2]: tọa độ y
- //object[i][3]: hướng (1 phải, 2 trên , 3 trái, 4 dưới)
- //
- // Chương trình chạy 1 lần
- void setup()
- {
- lcd.ON();
- lcd.SET(23, 0, 0, 0, 4);
- Xmin = 0;
- Xmax = 125;
- Ymin = 0;
- Ymax = 63;
- }
- //===========================================================
- ////////////////DI CHUYỂN GÀ////////////////////
- void di_chuyen_ga()
- {
- byte x_cu, y_cu, huong_cu;
- byte x_moi;
- x_cu = object[0][1];
- y_cu = object[0][2];
- huong_cu = object[0][3];
- //lấy thông tin
- if (huong_cu == 1) { //gà quay về bên phải
- x_moi = x_cu + 1;
- lcd.Plus_Bitmap(x_moi, y_cu, 16, 16, ga_mai_16x16, 0, MIRROR, BLACK);
- // xoay bitmap (mirror)
- //
- }
- if (huong_cu == 3) { //gà quay về bên trái
- x_moi = x_cu - 1;
- lcd.Plus_Bitmap(x_moi, y_cu, 16, 16, ga_mai_16x16, 0, NO_MIRROR, BLACK);
- // không xoay (non mirror)
- }
- //ghi lại thông tin
- object[0][1] = x_moi;
- lcd.display();
- } //vẽ gà
- void xoa_ga(byte x, byte y)
- {
- lcd.fillrect(x, y, 18, 18, DELETE);
- lcd.display();
- } //xóa gà
- void ga_di_va_de_trung()
- { // gà đi và đẻ trứng
- //////////// XỬ LÍ GÀ////////////
- //b1: cấp giấy khai sinh
- if (object[0][0] == 0) {
- // nếu không tồn tại gà thì phải tạo gà
- // cấp thuộc tính cho đối tượng gà
- object[0][1] = Xmin + 5; //hoành độ ban đầu
- object[0][2] = Ymin; // tung độ ban đầu
- object[0][3] = 1; //hướng sang bên phải
- object[0][0] = 1; //cấp quyền tồn tại
- }
- //b2: gà đi và quay đầu
- //lấy thông tin;
- if (object[0][1] <= Xmin) {
- // nếu gà đến biên trái thì phải quay đầu về bên phải
- object[0][3] = 1;
- //ghi lại thông tin
- }
- if (object[0][1] >= Xmax) {
- // nếu gà đến biên phải thì phải quay đầu về bên trái
- object[0][3] = 3;
- //ghi lại thông tin
- }
- ///////////GÀ ĐANG ĐI/////////
- // lấy tt
- xoa_ga(object[0][1], object[0][2]); //xóa ảnh cũ
- di_chuyen_ga(); // vẽ ảnh mới
- }
- //===============================================================
- ////////////////////TRỨNG RƠI//////////////////////
- void ve_trung(byte x, byte y)
- {
- lcd.bitmap(x, y, 6, 7, trung_6x7, BLACK);
- lcd.display();
- }
- void xoa_trung(byte x, byte y)
- {
- //xóa = hình chữ nhật màu trắng
- lcd.fillrect(x, y, 7, 7, DELETE);
- lcd.display();
- }
- void di_chuyen_trung(byte huong_cu, byte i)
- {
- byte x_cu, y_cu, x_moi, y_moi, ton_tai, huong_moi;
- //lây tt
- x_cu = object[i][1];
- y_cu = object[i][2];
- ton_tai = object[i][0];
- if ((huong_cu == 4) && (ton_tai == 1)) {
- //trứng rơi xuống dưới;
- y_moi = y_cu + 1;
- x_moi = x_cu;
- huong_moi = huong_cu;
- ve_trung(x_moi, y_moi);
- }
- //ghi tt
- object[i][1] = x_moi;
- object[i][2] = y_moi;
- object[i][3] = huong_moi;
- }
- void ve_trung_roi()
- {
- for (byte i = 1; i <= 3; i++) { // xét lần lượt 3 quả trứng
- /*
- if((object[i][3]==3)||(object[i][3]==1)){
- // nếu trứng i này đang bay theo parabol
- //thì phải nhảy sang trứng khác
- */
- //vẽ trứng rơi
- if (object[i][0] == 0) {
- // nếu không tồn tại trứng thì phải tạo trứng
- //b1: chuẩn bị sẵn tọa độ bằng random
- randomSeed(millis() % 5000);
- byte X_random;
- X_random = random(Xmin, Xmax);
- // lưu thông tin
- object[i][1] = X_random;
- //chương trình chỉ chạy 1 lần mà thôi
- }
- //khi gà đi đến vị trí chuẩn bị sẵn này, trứng bắt đầu được tạo và rơi
- if (object[i][1] == object[0][1]) { // thỏa mãn trùng tọa độ
- //b2: cấp giấy khai sinh
- // cấp thuộc tính cho đối tượng trứng
- //(nên nhớ đã có hoành độ Random)
- object[i][2] = Ymin + 10; // tung độ ban đầu
- object[i][3] = 4; //hướng xuống dưới
- object[i][0] = 1; //cấp quyền tồn tại
- }
- //b2 : trứng roi
- //lấy thông tin
- xoa_trung(object[i][1], object[i][2]); //xóa ảnh cũ
- di_chuyen_trung(object[i][3], i); // vẽ ảnh mới
- //b3: Trứng chạm đất và vỡ
- // hàm test nếu tung độ quá biên thì mất quả trứng đó
- if (object[i][2] >= Ymax) {
- object[i][0] = 0; // xóa quyền tồn tại
- }
- } //for
- }
- //========================================================
- void loop()
- {
- ga_di_va_de_trung();
- ve_trung_roi();
- delay(100);
- }
Kết quả test thử
6
Di chuyển nhân vật
- #include "ST7565_homephone.h"
- // add a bitmap library of a 16x16 fruit icon
- #include "bmps.h"
- ST7565 lcd(3, 4, 5, 6);
- #ifdef __AVR__
- #include <avr/io.h>
- #include <avr/pgmspace.h>
- #endif
- /** Nhân vật Cả Mẹo (22x18)pixel
- */
- const static unsigned char __attribute__((progmem)) ca_meo_22x18[] = {
- 0x60, 0x9C, 0x62, 0x01, 0x01, 0xC1, 0x26, 0x29, 0x29, 0x4B, 0x4E, 0x48, 0x70, 0x00, 0xC0, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0xC0, 0x00, 0x00, 0x01, 0x01, 0x02, 0xE4, 0xFF, 0xFE, 0xFE, 0xFE,
- 0xFC, 0x1C, 0x0E, 0x06, 0x1F, 0x21, 0x52, 0x44, 0x48, 0x52, 0x21, 0x1F, 0x00, 0x00, 0x64, 0x70,
- 0x2F,
- };
- /** Nhân vật Dần Béo (23x19)pixel
- */
- const static unsigned char __attribute__((progmem)) dan_beo_23x19[] = {
- 0x00, 0x00, 0x80, 0x43, 0xB2, 0x0F, 0x86, 0x44, 0x5C, 0xA2, 0xA6, 0x5C, 0x50, 0xB0, 0x40, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0xE3, 0xF4, 0xFA, 0xF0, 0xF7, 0x28, 0x78,
- 0xD8, 0xA8, 0xA8, 0xA4, 0x90, 0x58, 0x3E, 0x0A, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04, 0x07, 0x07,
- 0x06, 0x06, 0x05, 0x07, 0x07, 0x04,
- };
- /* mũi tên
- *
- */
- const static unsigned char __attribute__((progmem)) mui_ten_5x4[] = {
- 0x01, 0x07, 0x0F, 0x07, 0x01,
- };
- // mảng cho 2 nhân vật Cả mẹo(0) và Dần béo(1)
- byte object2[2][4] = { 0 };
- //object[i][0]: tồn tại hay không
- //object[i][1]: tọa độ x
- //object[i][2]: tọa độ y
- //object[i][3]: hướng (1 phải, 2 trên , 3 trái, 4 dưới)
- //
- byte chon_nv_A;
- byte chon_nv_B;
- //lựa chọn nhân vật, mặc định A được chọn trước
- byte Xmin, Ymin, Xmax, Ymax; //biên
- //cài đặt chân input
- #define fight_b A5
- #define select_b A4
- #define right_b A3
- #define up_b A2
- #define left_b A1
- #define down_b A0
- // Chương trình chạy 1 lần
- void setup()
- {
- Serial.begin(9600);
- lcd.ON();
- lcd.SET(23, 0, 0, 0, 4);
- pinMode(left_b, INPUT_PULLUP);
- pinMode(down_b, INPUT_PULLUP);
- pinMode(right_b, INPUT_PULLUP);
- pinMode(up_b, INPUT_PULLUP);
- pinMode(fight_b, INPUT_PULLUP);
- pinMode(select_b, INPUT_PULLUP);
- Xmin = 0;
- Xmax = 125;
- Ymin = 0;
- Ymax = 63;
- chon_nv_A = 1;
- chon_nv_B = 0;
- }
- ////////ĐIỀU KHIỂN 2 NHÂN VẬT//////////////////////
- void ve_nhan_vat(byte x, byte y, byte huong, byte i)
- {
- switch (i) {
- case 0:
- // vẽ Cả Mẹo
- if (huong == 1) {
- //quay về bên phải
- lcd.Plus_Bitmap(x, y, 22, 18, ca_meo_22x18, 0, NO_MIRROR, BLACK);
- }
- if (huong == 3) {
- //quay về bên trái
- lcd.Plus_Bitmap(x, y, 22, 18, ca_meo_22x18, 0, MIRROR, BLACK);
- }
- lcd.display();
- break;
- case 1:
- if (huong == 1) {
- //quay về bên phải
- lcd.Plus_Bitmap(x, y, 23, 19, dan_beo_23x19, 0, NO_MIRROR, BLACK);
- }
- if (huong == 3) {
- //quay về bên trái
- lcd.Plus_Bitmap(x, y, 23, 19, dan_beo_23x19, 0, MIRROR, BLACK);
- }
- lcd.display();
- //vẽ Dần Béo
- break;
- default:
- break;
- } //switch
- } // ve nhân vật
- void xoa_nhan_vat(byte x, byte y)
- {
- lcd.fillrect(x, y - 7, 24, 19 + 7, DELETE);
- lcd.display();
- } // xóa nhân vật
- void ve_mui_ten(byte x, byte y)
- {
- // mũi tên sẽ chỉ vào nhân vật i
- lcd.bitmap(x, y, 5, 4, mui_ten_5x4, BLACK);
- lcd.display();
- } // vẽ mũi tên
- void xoa_mui_ten(byte i)
- {
- lcd.fillrect(object2[i][1] - 5, object2[i][2] - 7, 20, 5, DELETE);
- lcd.display();
- } //xóa mũi tên
- void di_chuyen_nhan_vat(byte i)
- {
- byte x_cu, y_cu, huong_cu;
- byte x_moi;
- //lấy thông tin
- x_cu = object2[i][1];
- y_cu = object2[i][2];
- huong_cu = object2[i][3];
- if (huong_cu == 1) {
- //đi về bên phải
- x_moi = x_cu + 4;
- ve_nhan_vat(x_moi, y_cu, huong_cu, i);
- ve_mui_ten(x_cu + 5, y_cu - 7);
- }
- if (huong_cu == 3) {
- //đi về bên trái
- if (x_cu >= Xmin + 4 * 2) {
- //chống quá biên
- x_moi = x_cu - 4;
- }
- else {
- x_moi = x_cu;
- }
- ve_nhan_vat(x_moi, y_cu, huong_cu, i);
- ve_mui_ten(x_cu + 5, y_cu - 7);
- }
- //lưu thông tin
- object2[i][1] = x_moi;
- } //di chuyển nhân vật
- void xu_li_nhan_vat()
- {
- //b0: điều khiển lựa chọn nhân vật
- if (digitalRead(fight_b) == 0) { //fight được nhấn
- if (chon_nv_A == chon_nv_B) { // đề phòng A bằng B
- chon_nv_A = 1;
- chon_nv_B = 0;
- }
- if (chon_nv_A != chon_nv_B) { //để dảm bảo A khác B
- chon_nv_A = !chon_nv_A;
- chon_nv_B = !chon_nv_B;
- //đổi
- }
- } //if
- //b1: chọn nhân vật
- byte i = 0;
- if ((chon_nv_A == 1) && (chon_nv_B == 0)) {
- i = 0; // nhân vật Cả Mẹo
- }
- if ((chon_nv_A == 0) && (chon_nv_B == 1)) {
- i = 1; // nhân vật Dần Béo
- }
- if (digitalRead(fight_b) == 0) { //fight được nhấn
- xoa_mui_ten(!i);
- ve_mui_ten(object2[i][1] + 5, object2[i][2] - 7);
- }
- //b2: cấp giấy khai sinh (chỉ dùng khi start game)
- if (object2[i][0] == 0) {
- // nếu không tồn tại gà thì phải tạo gà
- // cấp thuộc tính cho đối tượng gà
- object2[i][1] = Xmin + 50 * i; //hoành độ ban đầu
- object2[i][2] = Ymax - 19; // tung độ ban đầu
- object2[i][3] = 1; //hướng về bên phải
- object2[i][0] = 1; //cấp quyền tồn tại
- }
- //b4: vẽ 2 nhân vật
- for (byte j = 0; j <= 1; j++) {
- ve_nhan_vat(object2[j][1], object2[j][2], object2[j][3], j);
- }
- //b5: điều khiển hướng nhân vật i
- byte read_button;
- read_button = lcd.Pullup_4(right_b, up_b, left_b, down_b);
- if (read_button == 1) { // nút right
- //ghi lại tt hướng
- object2[i][3] = 1;
- xoa_mui_ten(!i);
- xoa_nhan_vat(object2[i][1], object2[i][2]); //xóa ảnh cũ
- //lấy thông tin tọa độ để xóa
- di_chuyen_nhan_vat(i); // vẽ ảnh mới
- }
- if (read_button == 3) { // nút left
- object2[i][3] = 3;
- xoa_mui_ten(!i);
- xoa_nhan_vat(object2[i][1], object2[i][2]); //xóa ảnh cũ
- //lấy thông tin tọa độ để xóa
- di_chuyen_nhan_vat(i); // vẽ ảnh mới
- }
- } //xử lí nhân vật
- //==================================================
- void loop()
- {
- xu_li_nhan_vat();
- delay(50);
- }
Kết quả
7
Xử lí nảy trứng
Giải phương trình quỹ đạo bay Parabol.
Trong đoạn code này
- byte X0; // tọa độ đỉnh parabol
- f_y_moi = 1.0 + sq((float(X0) - float(x_moi)) / 4.0); //phương trình
Với quy ước màn hình, trục tung hệ tọa độ có chiều hướng xuống dưới. Kết quả ta được phương trình của chuyển động ném xiên.
Bay từ trái sang phải
- #include "ST7565_homephone.h"
- ST7565 lcd(3, 4, 5, 6); //RST, SCLK, A0, SID
- void setup()
- {
- lcd.ON();
- lcd.SET(23, 0, 0, 0, 4);
- }
- void loop()
- {
- float y;
- byte X0 = 60; // hoành độ đỉnh parabol
- for (byte x = 30; x < 100; x += 1) {
- y = 1.0 + sq((float(X0) - float(x)) / 4.0); //phương trình
- lcd.FillCircle(x, byte(y), 4, BLACK);
- lcd.Display();
- delay(1);
- lcd.Clear(); // xóa toàn màn hình
- }
- }
Bay từ phải sang trái
- #include "ST7565_homephone.h"
- ST7565 lcd(3, 4, 5, 6); //RST, SCLK, A0, SID
- void setup()
- {
- lcd.ON();
- lcd.SET(23, 0, 0, 0, 4);
- }
- void loop()
- {
- float y;
- byte X0 = 60; // hoành độ đỉnh parabol
- for (byte x = 100; x > 30; x -= 1) {
- y = 1.0 + sq((float(X0) - float(x)) / 4.0); //phương trình
- lcd.FillCircle(x, byte(y), 4, BLACK);
- lcd.Display();
- delay(1);
- lcd.Clear(); // xóa toàn màn hình
- }
- }
8
Kết
Code đã dài, sau khi kết hợp các module với nhau, ta được Game hoàn chỉnh