GAME-ST7565-LÀM GAME FLAPPY BIRD VỚI ARDUINO

Mô tả dự án: 

Nói tới Game này thì ai cũng biết, là một trong số những Game của người Việt có tiếng vang lớn trong vài năm  trước, cách chơi đơn giản,đồ họa 2D basic... vậy còn lập trình nó với ARDUINO thì sao nhỉ ?

FLAPPY BIRD

Vượt qua cái ngưỡng “đơn giản” đó đã khó ,  dành điểm số để đạt TOP thì còn hơn cả thử thách là những gì người chơi sẽ trải qua, chúng đã thực sự đem lại nhiều cung bậc cảm xúc cho người chơi.

May mắn thay, việc lập trình Game này còn dễ hơn cả chơi nó.devil Hãy cùng mình bắt đầu nhé.winkyes

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

ST7565 | Hướng dẫn sử dụng glcd ST7565 homephone và chia sẻ thư viện

Chuẩn bị phần cứng

Nối mạch

Bạn hãy tham khảo cách nối mạch và nút bấm tại bài viết giới thiệu lcd nhé:

Quản lí đối tượng

Bạn có thể cần đọc các bài viết về quản lí đối tượng Game :

Chương trình viết theo phong cách hướng đối tượng.

Chỉ có 2 loại đối tượng cần quan tâm.

Đối tượng
Chức năng, nhiệm vụ
Thuộc tính hình học
Thuộc tính khác.
Chú chim
Người dùng nhấn chạm vào màn hình ( với Project này là nhấn vào nút ấn), điều khiển chim bay lên, vượt qua các chướng ngại vật.
Tọa độ : X,Y
Sự tồn tại.: Sống/Chết.
Hướng : Bay lên/ bay xuống.
Ống nước
Là các chướng ngại vật, xuất hiện cả ở trên và dưới màn hình, chú chim bay và vượt qua các khe hở tạo bởi 2 ống nước.
Tọa độ: X,Y.
Sự tồn tại: Còn hoặc không còn ảnh hưởng tới chú chim.
Hướng: di chuyển từ phải sang trái.

Sự tồn tại

Như đã biết để điều khiển các ống nước và chú chim (các đối tượng) ta cần phải tìm cách quản lí chúng, bằng cách sử dụng bảng thống kê( biến X,Y,…) hoặc thực thể hóa thành với các thuộc tính gần gũi (hướng, tồn tại, kích thước, vị trí.). Mỗi đối tượng luôn mang theo những thuộc tính của riêng nó, việc tạo ra các đối tượng sẽ đi cùng với việc sử dụng thêm tài nguyên trên lưu trữ của ARDUINO (máy tính), cụ thể ở đây là tăng thêm các biến giá trị để lưu và tính toán trên chúng.

Trên hành trình của chú chim, chú phải bay qua rất nhiều ống nước rồi ghi điểm mỗi khi vượt qua,  ngoài đời thực, ngoài đời thực, nếu như có n ống nước, thì đó đương nhiên là n đối tượng , mỗi đối tượng cần 1 trang giấy để ghi lại thông tin thì ta cần n trang giấy cho từng đó đối tượng.

Trong lập trình Game, ta cần cụ thể hơn khi xem xét chúng:

Đối tượng cần quan tâm.
Đối tượng không cần quan tâm.
Chú chim .
Với những chiếc ống ống nước mà chim đã bay qua .
Chiếc ống nước gần chim nhất.
Với những ống nước ở quá xa so với chim.
Những gì cần vẽ lên màn hình.
Những đối tượng bị gắn mác  là “ không tồn tại”

Đó là ý nghĩa của “sự tồn tại”, nó giúp ta đơn giản hóa và tiết kiệm dữ liệu nhớ khi chỉ quan tâm quản lí đến các đối tượng "gần gũi"  nhất.

Đơn giản hóa vấn đề:

  • Hình dáng chi tiết của ống nước được phác họa bởi một hình chữ nhật đơn giản dạng ống .
  • Hệ quan sát của ta (Camera) gắn với hệ của chú chim, do đó , khi biểu diễn quá trình di chuyển  của chim , đối tượng này luôn giữ một hoành độ cố định, trong khi đó những chiếc ống nước sẽ di chuyển từ phải qua trái, tạo ra chuyển động tương đối giữa chim và cột.
  •  Sẽ không phải là n chiếc ống di chuyển,  mà sẽ là chú chim vượt qua chướng ngại vật n lần.

Khi lập trình, điều này có nghĩa là chỉ có một số ít ống nước cần quan tâm, cụ thể 6 ống nước di chuyển cách đều. Sau mỗi chu kì di chuyển (đi hết từ phải qua trái), chúng lại trở về vị trí xuất phát rồi lại di chuyển nhưng với độ cao thay đổi. Chúng ta dùng hàm Random() để khởi tạo giá trị độ cao một cách ngẫu nhiên tạo ra những chiếc ống khác nhau trên hành trình. Đây là ý tưởng để giải quyết vấn đề quản lí đối tượng như đã nêu.

Phần ý tưởng đã khá chi tiết, dù sao cũng chỉ là ý tưởng của mình mà thui. Bạn sẽ hiểu kĩ hơn khi đọc các đoạn Code bên dưới.

Class cơ sở

Chứa các thuộc tính chung nhất và phương thức truy cập (đọc/ghi) .

Các thuộc tính chung của các đối tượng Game (chim/ ống nước) bao gồm:

  • Vị trí : tọa độ x,y
  • Sự tồn tại.
  • Hướng.(đang di chuyển theo hướng nào)

Class “cột”

Ý mình “cột” là các cột ống nước.

Mỗi cột là một bộ ống nước xếp trên dưới . (Tuy vẽ là 2 ống nước nhưng chỉ quy định là 1 đối tượng mà thôi).

Phương thức

  • Vẽ/xóa cột: vẽ và xóa hình ảnh trên màn hình.
  • Tạo cột: đặt một độ cao Random() khi nó quay về điểm xuất phát.
  • Tạo một biến có tên “met” (mét) để đo quãng đường di chuyển của các ống nước, chúng ta dãn khoảng cách và xét tồn tại dựa vào quãng đường “met” này.
  • Gọi  Serial.println(met); // để  kiểm tra mét
  • Gọi   Serial.print(chiec_cot[i].get_ton_tai()); // để kiểm tra sự tồn tại của các đối tượng

Code tạo 3 cột di chuyển auto trên màn hình

#include "ST7565_homephone.h"

// add a bitmap library of a 16x16 fruit icon
#include "bmps.h"

ST7565 lcd(3, 4, 5, 6);

//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*/
#ifdef __AVR__
#include <avr/io.h>
#include <avr/pgmspace.h>
#endif

unsigned int met; // quãng đường đi được
// Chương trình chạy 1 lần
void setup()
{
    Serial.begin(9600);
    lcd.ON();
    lcd.SET(23, 0, 0, 0, 4);

    pinMode(fight_b, INPUT_PULLUP);

    lcd.clear();
}
class object {
    // lớp cơ sở

public:
    // biến toàn cục (dùng ở mọi nơi)
    byte Xmin = 0, Ymin = 0, Xmax = 125, Ymax = 63; //biên
    byte cot_max = 3; // số lượng cột trên màn hình
    byte do_rong = 21; //khoảng cách 2 cột trên và dưới
    // tập hàm ghi thông tin
    void set_ton_tai(boolean value) { ton_tai = value; }
    void set_x(byte value) { x = value; }
    void set_y(byte value) { y = value; }
    void set_huong(byte value) { huong = value; }
    //tập hàm lấy thông tin
    boolean get_ton_tai() { return ton_tai; }
    byte get_x() { return x; }
    byte get_y() { return y; }
    byte get_huong() { return huong; }
private:
    // chỉ 2 tập hàm thành viên viên trên mới sử đụng được 4 tham số này:
    boolean ton_tai;
    byte x;
    byte y;
    byte huong;
};
/*
class bird: public object{//kế thừa lớp object
  //...........
};//class
*/
class cot : public object { // kế thùa lớp object

public:
    void ve_cot(byte x, byte y, byte do_rong)
    {
        //do_rong là khoảng cách của 2 cột trên-dưới
        lcd.rect(x, Ymin, 7, y - Ymin, BLACK); //vẽ cột trên
        lcd.rect(x, y + do_rong, 7, Ymax - y, BLACK); //vẽ cột dưới
        lcd.display();
    }
    void xoa_cot(byte x, byte y, byte do_rong)
    {
        lcd.rect(x, Ymin, 7, y - Ymin, WHITE);
        lcd.rect(x, y + do_rong, 7, Ymax - y, WHITE);
    }
    cot()
    {
        // tạo đối tượng bằng constructor
    }
    void tao_cot()
    {

        //tạo random với tham số truyền vào là đồng hồ hệ thống
        unsigned int seed;
        seed = millis() % 1000;
        randomSeed(seed);
        byte y_random;
        y_random = random((Ymin + 10), (Ymax - 10));
        //lưu thông tin cho cột "đó"
        set_x(Xmax); //hoành độ ban đầu
        set_y(y_random); //tung độ đầu
        set_ton_tai(1); //cấp tồn tại
    }
    void di_chuyen_cot()
    { // chính cột đó

        //vẫn tồn tại

        //lấy thông tin
        byte x_cu = get_x(), y_cu = get_y(), x_moi; //tọa độ cũ-mới

        // dịch sang bên trái
        x_moi = x_cu - 1;
        //xóa hình cũ
        xoa_cot(x_cu, y_cu, do_rong);
        //vẽ hình mới
        ve_cot(x_moi, y_cu, do_rong);
        //lưu thông tin
        set_x(x_moi);
        if (get_x() == 250) {
            // nếu cột đi đến biên trái màn hình
            //thì không cho cột chạy nữa
            // x nhỏ nhất là 0, tuy nhiên nếu tiếp tục trừ x thì nó sẽ đém từ 254 ->0
            // đây là hiện tượng tràn số
            set_ton_tai(0); // tước quyền tồn tại
        }

    } //di chuyen cot
}; //class

object data; // lấy biến toàn cục
cot chiec_cot[3]; //tạo cột
void loop()
{

    for (byte i = 0; i < data.cot_max; i++) {

        if ((chiec_cot[i].get_ton_tai() == 0)) {
            if (met >= (127 / data.cot_max)) {
                //mõi cột được tạo khi đi được 1 đoạn cố định:127/(data.cot_max),
                //xắp sếp đều trên màn hình

                chiec_cot[i].tao_cot();
                met = 0; // dat lai
            }
        }
        else {
            chiec_cot[i].di_chuyen_cot();
        }
        Serial.print(chiec_cot[i].get_ton_tai());
    }

    delay(10);

    Serial.println(met);
    met++;
}

Kiểm tra một vài thông số

 

OK, thuật toán của chúng ta có tính tổng quát, ta có thể tùy chỉnh số lượng ống nước.

Ví dụ trên Cot_max = 3, ở đây mình cài Cot_max = 10 xem như thế nào ^^.

Đến dòng 37 sửa Cot_max=10; Đến dòng 124 sửa khởi tạo đối tượng bằng 10 ;

Kết quả

Có vẻ ARDUINO của chúng ta đã hơi Lag? Điều này có thể dễ hiểu vì nó phải xử lý 10 đối tượng cùng lúc !

Không sao, đó chỉ là vấn đề cấu hình máy chơi Game mà thôi.

Chúng ta có thể tùy chỉnh tốc độ chuyển khung hình bằng cách sửa lại giá trị của Delay();

Xem Serial có gì nào :

Class “bird”

Cũng giống như class “cot”, class”bird” sẽ kế thừa từ class cơ sở những thuộc tính chung nhất.

Các phương thức

  • Vẽ / xóa ảnh bitmap.
  • chim_bay(): dựa vào  các thông số cũ về hướng và tọa độ để cho chim bay.
  • dk_chim(): nhấn nút ấn để điều khiển hướng ( đặt hướng cho phương thức chim_bay())
  • “Sự tồn tại” của chim là thuộc tính giúp ta kiểm soát tiếp tục Game / đặt “GAME OVER”.

Chim bay lên :

Chim bay xuống: 

Code

#include "ST7565_homephone.h"
ST7565 lcd(3, 4, 5, 6);
//cài đặt chân input
#define fight_b A5

// Chương trình chạy 1 lần
void setup()
{
    lcd.ON();
    lcd.SET(21, 0, 0, 0, 4);
    pinMode(fight_b, INPUT_PULLUP);
}

#ifdef __AVR__
#include <avr/io.h>
#include <avr/pgmspace.h>
#endif

const static unsigned char __attribute__((progmem)) bird_up_15x8[] = {
    0x30, 0x58, 0x5E, 0xFB, 0x81, 0x85, 0x9B, 0x91, 0x91, 0x7D, 0x71, 0x7E, 0x60, 0x60, 0x60,
};

const static unsigned char __attribute__((progmem)) bird_down_15x8[] = {
    0x1E, 0x32, 0x22, 0x6E, 0x99, 0x85, 0x9B, 0x91, 0x91, 0x7D, 0x71, 0x7E, 0x60, 0x60, 0x60,
};

class object {
    // lớp cơ sở

public:
    // biến toàn cục (dùng ở mọi nơi)
    byte Xmin = 0, Ymin = 0, Xmax = 125, Ymax = 63; //biên
    // tập hàm ghi thông tin
    void set_ton_tai(boolean value) { ton_tai = value; }
    void set_x(byte value) { x = value; }
    void set_y(byte value) { y = value; }
    void set_huong(byte value) { huong = value; }
    //tập hàm lấy thông tin
    boolean get_ton_tai() { return ton_tai; }
    byte get_x() { return x; }
    byte get_y() { return y; }
    byte get_huong() { return huong; }
private:
    // chỉ 2 tập hàm thành viên viên trên mới sử đụng được 4 tham số này:
    boolean ton_tai;
    byte x;
    byte y;
    byte huong;
};
class bird : public object { //kế thừa lớp object
public:
    bird()
    {
        // hàm tạo constructor
        set_x((Xmax - Xmin) / 3); //hoành độ ban đàu
        set_y(Ymin + 20); // tung độ ban đàu
        set_huong(4); // hướng ban đầu(xuống)
    }
    //vẽ chim
    void ve_chim(byte x, byte y, byte huong)
    {
        if (huong == 2) {
            //hướng lên
            lcd.bitmap(x, y, 15, 8, bird_up_15x8, BLACK);
        }

        if (huong == 4) {
            //hướng xuống
            lcd.bitmap(x, y, 15, 8, bird_down_15x8, BLACK);
            //xoay ảnh 270 độ
        }
        lcd.display();
    } //
    void xoa_chim(byte x, byte y)
    {
        lcd.fillrect(x, y, 15, 8, DELETE);
        lcd.display();

    } //

    void chim_bay()
    {

        //lấy thông tiin
        byte x_cu = get_x(), y_cu = get_y(), huong_cu = get_huong();
        byte x_moi, y_moi, huong_moi;

        if (huong_cu == 2) {
            //bay lên
            y_moi = y_cu - 4;
        }
        if (huong_cu == 4) {
            //bay xuống
            y_moi = y_cu + 2;
        }
        //xóa ảnh cũ:
        xoa_chim(x_cu, y_cu);
        //vẽ ảnh mới
        ve_chim(x_cu, y_moi, huong_cu);
        //lưu thông tin
        set_y(y_moi);
    }
    void dk_chim()
    {
        //điều khiển chim
        byte bay_len;
        bay_len = digitalRead(fight_b); // đọc giá trị button
        if (bay_len == 0) { //nhấn nút fight
            //bay lên
            //ghi thông tin
            set_huong(2);
        }

        if (bay_len == 1) { //ko nhấn
            //bay lên
            //ghi thông tin
            set_huong(4);
        }
        //cho chim bay
        chim_bay();
    }
}; //class bird

bird con_chim; // tạo thực thể con_chim
void loop()
{

    con_chim.dk_chim();
    delay(200);
}

TEST VA CHẠM VÀ HOÀN THIỆN GAME

Bước cuối là xét va chạm.

Vì có chúng ta có ít đối tượng ống nước (từ 3 -> 10 cột tùy bạn) nên chúng ta sẽ xét cả thảy tọa độ và độ cao của tất cả các ống nước với chim.

Copy đoạn code này và chơi thôi !

//GAME FLAPPY BIRD
// Viết bởi:  Thái Sơn , 6/7/2016
// Chia sẻ tại arduino.vn - 3/2/2017
#include "ST7565_homephone.h"
// add a bitmap library of a 16x16 fruit icon
#include "bmps.h"

#ifdef __AVR__
#include <avr/io.h>
#include <avr/pgmspace.h>
#endif

ST7565 lcd(3, 4, 5, 6);

//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*/
const static unsigned char __attribute__((progmem)) bird_up_15x8[] = {
    0x30, 0x58, 0x5E, 0xFB, 0x81, 0x85, 0x9B, 0x91, 0x91, 0x7D, 0x71, 0x7E, 0x60, 0x60, 0x60,
};

const static unsigned char __attribute__((progmem)) bird_down_15x8[] = {
    0x1E, 0x32, 0x22, 0x6E, 0x99, 0x85, 0x9B, 0x91, 0x91, 0x7D, 0x71, 0x7E, 0x60, 0x60, 0x60,
};

unsigned int met; // quãng đường đi được
// Chương trình chạy 1 lần
void setup()
{
    lcd.ON();
    lcd.SET(23, 0, 0, 0, 4);

    pinMode(fight_b, INPUT_PULLUP);

    lcd.clear();
    start_game();
}
//=================================================
class object {
    // lớp cơ sở

public:
    // biến toàn cục (dùng ở mọi nơi)
    byte Xmin = 0, Ymin = 0, Xmax = 125, Ymax = 63; //biên
    byte cot_max = 4; // số lượng cột trên màn hình
    byte do_rong = 28; //khoảng cách 2 cột trên và dưới
    static unsigned int diem, diem_cao; // biến tĩnh
    // tập hàm ghi thông tin
    void set_ton_tai(boolean value) { ton_tai = value; }
    void set_x(byte value) { x = value; }
    void set_y(byte value) { y = value; }
    void set_huong(byte value) { huong = value; }
    //tập hàm lấy thông tin
    boolean get_ton_tai() { return ton_tai; }
    byte get_x() { return x; }
    byte get_y() { return y; }
    byte get_huong() { return huong; }
private:
    // chỉ 2 tập hàm thành viên viên trên mới sử đụng được 4 tham số này:
    boolean ton_tai;
    byte x;
    byte y;
    byte huong;
};
unsigned int object::diem = 0;
unsigned int object::diem_cao = 0;

//=================================================

class bird : public object { //kế thừa lớp object
public:
    bird()
    { // hàm tạo constructor
        tao_chim();
    }
    void tao_chim()
    {
        set_x((Xmax - Xmin) / 3); //hoành độ ban đàu
        set_y(Ymin + 20); // tung độ ban đàu
        set_huong(4); // hướng ban đầu(xuống)
        set_ton_tai(1);
    }
    //vẽ chim
    void ve_chim(byte x, byte y, byte huong)
    {
        if (huong == 2) {
            lcd.bitmap(x, y, 15, 8, bird_up_15x8, BLACK);
        }
        if (huong == 4) {
            lcd.bitmap(x, y, 15, 8, bird_down_15x8, BLACK);
        }
        lcd.display();
    } //
    void xoa_chim(byte x, byte y)
    {
        lcd.fillrect(x, y, 15, 8, DELETE);
        lcd.display();
    } //
    void chim_bay()
    {
        if (get_ton_tai() == 0) {
            tao_chim();
        }
        else {
            //lấy thông tiin
            byte x_cu = get_x(), y_cu = get_y(), huong_cu = get_huong();
            byte x_moi, y_moi, huong_moi;

            if (huong_cu == 2) {
                y_moi = y_cu - 4;
            } // bay lên
            if (huong_cu == 4) {
                y_moi = y_cu + 2;
            } //bay xuống
            xoa_chim(x_cu, y_cu); //xóa ảnh cũ:
            ve_chim(x_cu, y_moi, huong_cu); //vẽ ảnh mới
            set_y(y_moi); //lưu thông tin
        }
    }
    void dk_chim()
    { //điều khiển chim
        byte bay_len;
        bay_len = digitalRead(fight_b); // đọc giá trị button
        if (bay_len == 0) {
            set_huong(2);
        }
        if (bay_len == 1) {
            set_huong(4);
        }
        chim_bay(); //cho chim bay
    }
}; //class
//================================================
class cot : public object { // kế thùa lớp object

public:
    void ve_cot(byte x, byte y, byte do_rong)
    {
        //do_rong là khoảng cách của 2 cột trên-dưới
        lcd.rect(x, Ymin, 7, y - Ymin, BLACK); //vẽ cột trên
        lcd.rect(x, y + do_rong, 7, Ymax - y, BLACK); //vẽ cột dưới
        lcd.display();
    }
    void xoa_cot(byte x, byte y, byte do_rong)
    {
        lcd.rect(x, Ymin, 7, y - Ymin, DELETE);
        lcd.rect(x, y + do_rong, 7, Ymax - y, DELETE);
        lcd.display();
    }
    cot()
    {
        // tạo đối tượng bằng constructor
    }
    void tao_cot()
    {

        //tạo random với tham số truyền vào là đồng hồ hệ thống
        unsigned int seed;
        seed = millis() % 1000;
        randomSeed(seed);
        byte y_random;
        y_random = random((Ymin + 10), (Ymax - 10));
        //lưu thông tin cho cột "đó"
        set_x(Xmax); //hoành độ ban đầu
        set_y(y_random); //tung độ đầu
        set_ton_tai(1); //cấp tồn tại
    }
    void di_chuyen_cot()
    { // chính cột đó
        //lấy thông tin
        byte x_cu = get_x(), y_cu = get_y(), x_moi; //tọa độ cũ-mới
        // dịch sang bên trái
        x_moi = x_cu - 1;
        xoa_cot(x_cu, y_cu, do_rong);
        ve_cot(x_moi, y_cu, do_rong);
        set_x(x_moi);
        if (get_x() == 250) {
            set_ton_tai(0); // tước quyền tồn tại
        }

    } //di chuyen cot

    boolean va_cham(object& chim)
    {
        //hàm kiểm tra chạm của cột i với chim
        //lấy thông tin
        int hieu_x, hieu_y;
        hieu_x = chim.get_x() - get_x(); // x chim trừ x cột i, 12 là độ rộng ảnh bitmap
        hieu_y = chim.get_y() - get_y(); // y chim trừ y cột i
        // và nếu chim không rơi xuống đất thì cũng ko  có va chạm
        if ((hieu_x >= (-12)) && (hieu_x <= 7)) { // chim đang bay trong cột i
            //(12 là độ rộng ảnh bitmap, 7 là đường kính cột)

            if ((hieu_y > 0) && (hieu_y < (do_rong - 8))) {
                // chim đang bay trong vùng  không có cột
                return 0; // không va trạm
            }
            else {
                return 1; // trả về có
            }
        }
        else { // chim không bay trong cột i
            return 0; // không va chạm
        }
    } // va cham
}; //class

//======================================================
class computer : public object { // lớp này để tính toán
public:
    void in_diem()
    {
        if (diem >= diem_cao) {
            diem_cao = diem;
        }
        lcd.number_ulong(Xmin + 5, 0, diem, ASCII_NUMBER, BLACK);
        lcd.display();
        lcd.number_ulong(Xmax - 15, 0, diem_cao, ASCII_NUMBER, BLACK);
        lcd.display();
    }
    void lap_vo_han()
    {
        // dừng dòng chảy chính bằng vòng lặp vô hạn
        //thoát lặp khi nút Fight được nhấn
        int y = 55;
        lcd.Asc_String(10, y + 2, Asc("Fight!"), BLACK);
        lcd.display();
        while (digitalRead(fight_b) != 0) {
            lcd.rect(8, 55, 40, 10, BLACK);
            lcd.display();
            if (digitalRead(fight_b) == 0) {
                break; // thoát ngay
            }
            delay(250);

            lcd.rect(8, y, 40, 10, WHITE);
            lcd.display();

            if (digitalRead(fight_b) == 0) {
                break; // thoát ngay
            }
            delay(250);
        }

    } //đóng lặp vô hạn
    void gameover()
    {

        //reset điểm
        diem = 0;
        // chờ nhấn nut fight
        lap_vo_han();
        //reset màn hình
        lcd.clear();
    }
};
//============================================
computer co;
bird con_chim; // tạo thực thể con_chim
cot chiec_cot[4]; //tạo cột
object data; // lấy biến toàn cục
void loop()
{
    //di chuyển chim
    con_chim.dk_chim();
    //di chuyển cột

    for (byte i = 0; i < data.cot_max; i++) {

        if ((chiec_cot[i].get_ton_tai() == 0)) {
            if (met >= (127 / data.cot_max)) {
                //mõi cột được tạo khi đi được 1 đoạn cố định:127/(data.cot_max), xắp sếp đều trên màn hình

                chiec_cot[i].tao_cot();
                met = 0; // dat lai
            }
        }
        else {
            chiec_cot[i].di_chuyen_cot();
        }

        // mở rộng hàm for

        ////////////va chạm và tính điểm//////////////

        //khi hoành độ  của chim == hoành độ cột i, cộng điểm 1 điểm
        if (con_chim.get_x() == chiec_cot[i].get_x()) {
            data.diem++;
        }
        // nếu có va chạm hoặc chim chạm đát thì gameover

        if ((chiec_cot[i].va_cham(con_chim) == 1) || (con_chim.get_y() >= data.Ymax)) {
            co.gameover();
            con_chim.set_ton_tai(0);
            for (byte i = 0; i < data.cot_max; i++) {
                chiec_cot[i].set_ton_tai(0);
                chiec_cot[i].set_x(0);
            }
            break;
        }
        // hiển thj điểm

    } //for

    co.in_diem();
    delay(10);

    met++;
}
void start_game()
{
    lcd.rect(8, 22, 118, 11, BLACK);
    lcd.Asc_String(10, 24, Asc("A R D U I N O . V N"), BLACK);
    lcd.rect(58, 54, 70, 11, BLACK);
    lcd.Asc_String(60, 55, Asc("By Thai Son"), BLACK);

    lcd.display();
    co.lap_vo_han();

    lcd.clear();
}

Kết

Game là để giải trí, với Flappy Bird, nó còn cho ta một thông điệp đáng quý trong cuộc sống về sự kiên trì: “Có công mài sắt, có ngày nên kim”

Chúc các bạn gặt hái được nhiều kĩ năng với C++ và Gaming với arduino cheekywinkyesenlightened

<Thái Sơn>

lên
8 thành viên đã đánh giá bài viết này hữu ích.
Từ khóa: 
Các dự án được truyền cảm hứng

Điều khiển 8 đèn LED qua wifi, sử dụng Arduino và ESP8266

Với mục đích giúp các bạn tiếp cận với các thiết bị IOT gần hơn. Hôm nay mình sẽ hướng dẫn các bạn viết chương trình điều khiển 8 LED qua mạng wifi. Và hơn thế nữa, nếu kết hợp với VPN hoặc mở port thì chúng ta có thể làm hơn thế nữa!

lên
43 thành viên đã đánh giá bài viết này hữu ích.
Các bài viết cùng tác giả

Bộ lọc Kalman – giải pháp chống nhiễu tuyệt vời cho mọi dự án sử dụng cảm biến

Rõ ràng khi ta sử dụng cảm biến, giá trị trả về từ  chúng luôn thay đổi quanh vị trí cân bằng dù là rất nhỏ, và bạn biết nguyên nhân của hiện tượng này  là do nhiễu, bạn luôn muốn loại bỏ nhiễu nhưng việc đó dường như ngoài tầm với của bạn.(-.-)… Đừng lo, chúng ta đã có giải pháp, bấm đọc bài viết này thôi nào!

lên
52 thành viên đã đánh giá bài viết này hữu ích.

AVR-OSCILLOSCOPE - Tự làm Máy hiện sóng điện tử trên nền ARDUINO với giá chưa đến 300 nghìn

Ở phiên bản này, máy hiện sóng AVR-OSCILLOSCOPE của mình có thể được hiển thị lên 2 lcd thông dụng là NOKIA5110 hoặc lcd ST7565 Homephone .

Đây là thiết bị hiện sóng đa năng, mạnh mẽ và vô cùng gọn nhẹ. Các bạn hoàn toàn tự làm nó ngay lại công xưởng chế tạo tại nhà của mình với giá thành cực rẻ (chưa đến 300k - tính luôn Arduino Uno). 

lên
14 thành viên đã đánh giá bài viết này hữu ích.