GAME-ST7565 - Làm Game xe tăng với Arduino

Mô tả dự án: 

Hôm nay sẽ là Game xe tăng – TANK WAR – tựa Game quen thuộc nhưng đầy hấp hẫn. Điểm đặc biệt là bạn có chơi ở chế độ cân TEAM 1-25 cực FUN devil.

TANK WAR

Sử dụng 4 nút điều khiển hướng để di chuyển xe tăng của ta, nhấn OK để khai hỏa.

Thuộc tính: Mỗi xe tăng chỉ được bắn 1 phát một cho đến khi đạn bay đến hết Biên thì mới được phép khai hỏa lần 2. Tốc độ của đạn và xe tăng của ta nhanh hơn so với địch.

Hỗ trợ xây xựng MAP theo sở thích, có thể tùy chỉnh số lượng địch là các ưu điểm của phiên bản này.

Chuẩn bị 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é:

CODE

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

Các kĩ năng lập trình mình sẽ không giới thiệu nữa.

Đây là Soure code hoàn thiện cuối cùng của mình, các bạn tải về rồi chiến thui.

Mình cũng đã ghi chú rất chi tiết trong quá trình làm game, hãy đọc nó để hiểu thêm ý tưởng của mình nhé.

/////////

////////
////////
// GAME NÀY ĐƯỢC VIẾT BỞI :PHÙNG THÁI SƠN-10:28pm 5/9/2016
// GAME ĐƯỢC UP CHÍNH THỨC TẠI ARDUINO.VN  4/2/2017
//SỬ DỤNG 6 BUTTON NỐI PULLUP
// VÀO HÀM setup, ve_nen,  ĐỂ CÀI ĐẶT THEO SỞ THÍCH NHÉ
//không bấm quá nhiều nút cùng lúc
#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

const static unsigned char __attribute__((progmem)) tank_ta_6x6[] = {

    /* page 0 (lines 0-7) */
    0x3f, 0x3f, 0x3f, 0x3f, 0xc, 0xc,
};
const static unsigned char __attribute__((progmem)) tank_dich_6x6[] = {

    /* page 0 (lines 0-7) */
    0x33, 0x33, 0x3f, 0x3f, 0xc, 0xc,
};
//số lượng tank lớn nhất , trong đó có cả tank ta
//tank_dich_max là bao nhiêu tùy bạn 1,2,..,7.(5 thui)
// mảng tĩnh sẽ ngốn Ram của bạn đấy
#define tank_dich_max 20
//  20 tank địch
byte tank[1 + tank_dich_max][10]; // ma trận mảng xe tăng ( mảng 2 chiều), (mảng tĩnh nhé)
// (cứ mỗi x, y trong c[x][y] thì ta mới được 1 ô giá trị)
// tổng cộng cần 5 tanks nhân 9 thuộc tính = 45 biến byte !
// mảng đầu quy định biển số xe thứ i từ 0 (ta) 1,2,3,4,..,tank_dich_max (địch)
// mảng sau quy định thuộc tính của xe thứ i, bao gồm
//tank[i][0]: trạng thái xe còn hay mất
// tank[i][1]: hoành độ xe i
// tank[i][2]: tung độ xe i
// tank[i][3]: hướng xe ĐÔNG-1, BẮC-2, TÂY-3, NAM-4
//tank[i][4]: số bước chân (pixel) mà xe đã đi
//tank[i][5]: hoành độ đạn đang bay của xe i
//tank[i][6]: tung độ đạn đang bay của xe i, tung độ có chiều dương hướng xuống nhá
//tank[i][7]: hướng viên đạn đang bay của xe i
//tank[i][8]: trạng thái đạn còn hay mất
// hướng ĐÔNG-1, BẮC-2, TÂY-3, NAM-4

byte Xmin, Xmax, Ymin, Ymax; // giới hạn khung hình:biên

unsigned int diem;

unsigned int delay_toc_do;
byte toc_do_dan, toc_do_dich, toc_do_ta;
// Chương trình chạy 1 lần
void setup()
{
    //||||||||||||||||||||||||||||||||||||||
    //Serial.begin(9600);
    Serial.println(getMemoryFree()); //Kiểm tra RAM

    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);
    //đặt ở chế độ sử dụng nội trở
    /*
// nếu muốn dùng điện trở ngoài thì xóa bên trên đi, copy -dán đoạn này thay vào
pinMode(left_b,INPUT);       
pinMode(down_b,INPUT);
pinMode(right_b,INPUT); 
pinMode(up_b,INPUT); 
pinMode(fight_b,INPUT); 
pinMode(select_b,INPUT);
*/
    //biên
    Xmin = 3; // min=1
    Xmax = 125; //max=127
    Ymin = 2; //min=1
    Ymax = 61; //max=62
    // chu kỳ delay, tốc đọ khung hình
    // auto chỉnh delay theo số lượng tank

    if (tank_dich_max <= 25) {
        //bạn đã cài tank_dich_max = bao nhiêu thế hử, 50 con sao?
        //sức chịu đựng của arduino cũng có hạn thui
        delay_toc_do = (50 - tank_dich_max * 2);
    }
    else {
        delay_toc_do = 5;
    }
    // tốc độ đạn, địch, ta
    toc_do_dan = 4;
    toc_do_dich = 1;
    toc_do_ta = 3;
    // điểm ban đầu
    diem = 0;
    /* BẬT CỔNG SERIAL ĐỂ XEM THÔNG BÁO
// kiểm tra mảng
Serial.begin(9600);
// xuống dưới cùng phá  ngoặc để mở thông báo */
} // đóng set up
void ve_nen()
{
    //||||||||||||||||||||||||||||||||||||||||||
    //Bạn có thể vẽ bất cứ thứ gì lên màn hình khi nó
    //start game vào đây
    lcd.rect(8, 22, 118, 11, BLACK);
    lcd.Asc_String(10, 23, 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();

    //||||||||||||||||||||||||||||||||||||||||||
};
void ve_tank_dich(byte x, byte y, byte huong)
{

    //buoc1: xóa
    xoa_xe(x, y);
    //bước 2: vẽ cái mới
    switch (huong) {
    case 1:
        // hướng đông

        lcd.fillrect(x, y, 4, 2, BLACK);
        lcd.fillrect(x + 2, y + 2, 4, 2, BLACK);
        lcd.fillrect(x, y + 4, 4, 2, BLACK);
        /*
lcd.Plus_Bitmap(x,y,6,6,tank_dich_6x6,0,NO_MIRROR, 0);// không xoay ảnh
//lúc chạy lúc không, có lẽ là do tốc độ độc dữ liệu trong bộ nhớ Flash
*/

        break;
    case 2:
        // hướng bắc
        lcd.fillrect(x, y + 2, 2, 4, BLACK);
        lcd.fillrect(x + 2, y, 2, 4, BLACK);
        lcd.fillrect(x + 4, y + 2, 2, 4, BLACK);
        /*
lcd.Plus_Bitmap(x,y,6,6,tank_dich_6x6,90,NO_MIRROR, 0);//  xoay ảnh 90
//lúc chạy lúc không, có lẽ là do tốc độ độc dữ liệu trong bộ nhớ Flash
*/
        break;

    case 3:
        // hướng tây
        lcd.fillrect(x + 2, y, 4, 2, BLACK);
        lcd.fillrect(x, y + 2, 4, 2, BLACK);
        lcd.fillrect(x + 2, y + 4, 4, 2, BLACK);
        /*
lcd.Plus_Bitmap(x,y,6,6,tank_dich_6x6,180,NO_MIRROR, 0);//  xoay ảnh 90
//lúc chạy lúc không, có lẽ là do tốc độ độc dữ liệu trong bộ nhớ Flash
*/
        break;

    case 4:
        // hướng nam
        lcd.fillrect(x, y, 2, 4, BLACK);
        lcd.fillrect(x + 2, y + 2, 2, 4, BLACK);
        lcd.fillrect(x + 4, y, 2, 4, BLACK);
        /*
lcd.Plus_Bitmap(x,y,6,6,tank_dich_6x6,270,NO_MIRROR, 0);//  xoay ảnh 90
//lúc chạy lúc không, có lẽ là do tốc độ độc dữ liệu trong bộ nhớ Flash
*/
        break;
    default:
        break;
    } // đóng switch
    lcd.display();

}; //đóng ve_tank_dich
/*
switch tương đương với dòng lệnh sau :
switch chỉ chấp nhận 2 kiểu biến đưa vào 
là char hoặc int
neu la char thi phai viet : case 'a':.....case'b':...
if (huong==2){// hướng đông
lcd.fillrect(x,y,4,2,BLACK);
lcd.fillrect(x+2,y+2,4,2,BLACK);
lcd.fillrect(x,y+4,4,2,BLACK);
}
if (huong==2){ // hướng bắc
lcd.fillrect(x,y+2,2,4,BLACK);
lcd.fillrect(x+2,y,2,4,BLACK);
lcd.fillrect(x+4,y+2,2,4,BLACK);
}

if (huong==3){ // hướng tây
lcd.fillrect(x+2,y,4,2,BLACK);
lcd.fillrect(x,y+2,4,2,BLACK);
lcd.fillrect(x+2,y+4,4,2,BLACK);
}

if (huong==4){ // hướng nam
lcd.fillrect(x,y,2,4,BLACK);
lcd.fillrect(x+2,y+2,2,4,BLACK);
lcd.fillrect(x+4,y,2,4,BLACK);
}
*/

void ve_tank_ta(byte x, byte y, byte huong)
{
    //buoc1: xóa
    xoa_xe(x, y);
    //bước 2: vẽ cái mới
    switch (huong) { // đổi về kiểu kí tự
    case 1:
        // hướng đông
        lcd.fillrect(x, y, 4, 2, BLACK);
        lcd.fillrect(x, y + 2, 6, 2, BLACK);
        lcd.fillrect(x, y + 4, 4, 2, BLACK);
        break;
    case 2:
        // hướng bắc
        lcd.fillrect(x, y + 2, 2, 4, BLACK);
        lcd.fillrect(x + 2, y, 2, 6, BLACK);
        lcd.fillrect(x + 4, y + 2, 2, 4, BLACK);
        break;

    case 3:
        // hướng tây
        lcd.fillrect(x + 2, y, 4, 2, BLACK);
        lcd.fillrect(x, y + 2, 6, 2, BLACK);
        lcd.fillrect(x + 2, y + 4, 4, 2, BLACK);
        break;

    case 4:
        // hướng nam
        lcd.fillrect(x, y, 2, 4, BLACK);
        lcd.fillrect(x + 2, y, 2, 6, BLACK);
        lcd.fillrect(x + 4, y, 2, 4, BLACK);
        break;
    default:
        break;
    } //đóng switch
    lcd.display();

}; //đóng ve_tank_ta
void no(byte x, byte y)
{
    // nổ xe tăng
    //tạo một thảm hình vuông
    byte a, b;

    for (a = x - 2; a <= x + 6; a += 4) {

        for (b = y - 2; b <= y + 6; b += 4) {
            lcd.fillrect(a, b, 2, 2, BLACK);
        }
    }

    lcd.display();
}; //đóng nổ xe tăng
byte kiem_tra_vat_can(byte x, byte y, byte huong)
{
    byte xi, yi;

    //đôi lúc phía trước có vật cản nhưng không quay đầu
    //đó là vì ở lần duyệt đầu return kết thúc for ngay lập tức và trả về 0
    // thế cũng hay, mình không muốn đổi lại code nữa
    switch (huong) { //đổi về kiểu char

    case 1:
        //phía dong_thì quét phía bên phải
        xi = x + 7;
        for (yi = y - 1; yi <= y + 5; yi++) {
            if (lcd.getpixel(xi, yi) == 1) {
                return 1;
            }
            else {
                return 0;
            }
        }
        break;
    case 2:
        //phía bắc_thì quét phía bên trên
        yi = y - 2;
        for (xi = x - 1; xi <= x + 6; xi++) {
            if (lcd.getpixel(xi, yi) == 1) {
                return 1;
            }
            else {
                return 0;
            }
        }

        break;
    case 3:
        //phía tây_thì quét phía bên trái
        xi = x - 2;
        for (yi = y - 1; yi <= y + 6; yi++) {
            if (lcd.getpixel(xi, yi) == 1) {
                return 1;
            }
            else {
                return 0;
            }
        }
        break;
    case 4:

        //phía nam_thì quét phía bên dưới
        yi = y + 7;
        for (xi = x - 1; xi < x + 6; xi++) {
            if (lcd.getpixel(xi, yi) == 1) {
                return 1;
            }
            else {
                return 0;
            }
        }
        break;
    default:
        break;
    } //đóng switch
}; // đóng phát hiện vạt cản

void nhich_ve_phia_truoc_dich(byte x, byte y, byte huong, byte i)
{
    byte xi, yi, hi;
    // bước 1: xóa xe
    xoa_xe(x, y);
    //bước 2: vẽ xe mới
    switch (huong) {
    case 1:
        xi = x + toc_do_dich;
        yi = y;
        hi = 1;
        ve_tank_dich(xi, yi, hi); //nhích sang bên phải
        break;

    case 2:

        xi = x;
        yi = y - toc_do_dich;
        hi = 2;
        ve_tank_dich(xi, yi, hi); //nhích lên phía trên

        break;

    case 3:
        xi = x - toc_do_dich;
        yi = y;
        hi = 3;
        ve_tank_dich(xi, yi, hi); //nhích sang bên trái

        break;

    case 4:
        xi = x;
        yi = y + toc_do_dich;
        hi = 4;
        ve_tank_dich(xi, yi, hi); //nhích xuống phía dưới
        break;
    default:
        break;
    } //đóng switch

    //ghi vào mảng
    tank[i][1] = xi;
    tank[i][2] = yi;
    tank[i][3] = hi;

}; // đóng nhích về phía trước

void nhich_ve_phia_truoc_ta(byte x, byte y, byte huong)
{

    byte xi, yi, hi;
    // bước 1: xóa xe
    xoa_xe(x, y);
    //bước 2: vẽ xe mới
    switch (huong) {

    case 1:
        xi = x + toc_do_ta;
        yi = y;
        hi = 1;
        ve_tank_ta(xi, yi, hi); //nhích sang bên phải
        break;
    case 2:

        xi = x;
        yi = y - toc_do_ta;
        hi = 2;
        ve_tank_ta(xi, yi, hi); //nhích lên phía trên

        break;

    case 3:
        xi = x - toc_do_ta;
        yi = y;
        hi = 3;
        ve_tank_ta(xi, yi, hi); //nhích sang bên trái
        break;

    case 4:
        xi = x;
        yi = y + toc_do_ta;
        hi = 4;
        ve_tank_ta(xi, yi, hi); //nhích xuống phía dưới
        break;
    default:
        break;
    } //đóng switch
    //ghi vào mảng
    tank[0][1] = xi;
    tank[0][2] = yi;
    tank[0][3] = hi;
}; // đóng ta nhích về phía trước
void ve_dan(byte x_dan, byte y_dan, byte huong_dan, byte i)
{
    byte xi, yi, hi;

    //bước 1: xóa cái cũ
    xoa_dan(x_dan, y_dan);
    //bước 2: vẽ 1 cái mới
    switch (int(huong_dan)) {
    case 1:
        // đạn bay về bên phải

        xi = x_dan + toc_do_dan;
        yi = y_dan;
        hi = 1;
        lcd.fillrect(xi, yi, 2, 2, BLACK);
        break;
    case 2:
        // đạn bay về PHÍA trên

        xi = x_dan;
        yi = y_dan - toc_do_dan;
        hi = 2;
        lcd.fillrect(xi, yi, 2, 2, BLACK);
        break;
    case 3:
        // đạn bay về bên trái

        xi = x_dan - toc_do_dan;
        yi = y_dan;
        hi = 3;
        lcd.fillrect(xi, yi, 2, 2, BLACK);
        break;
    case 4:
        // đạn bay về phía dưới

        xi = x_dan;
        yi = y_dan + toc_do_dan;
        hi = 4;
        lcd.fillrect(xi, yi, 2, 2, BLACK);
        break;
    default:
        break;
    } //đóng switch
    lcd.display();

    //ghi vào mảng
    tank[i][5] = xi;
    tank[i][6] = yi;
    tank[i][7] = hi;

}; // đóng vẽ đạn

void xoa_dan(byte x_dan, byte y_dan)
{

    // xóa một viên đạn ở tọa độ x,y bằng 1 hình chữ nhật màu trắng
    lcd.fillrect(x_dan, y_dan, 2, 2, WHITE);
    lcd.display();
}; // đóng xóa đạn

void xoa_xe(byte x, byte y)
{
    // xóa một xe tăng ở tọa độ x,y bằng 1 hình chữ nhật màu trắng

    lcd.fillrect(x, y, 6, 6, WHITE); // xóa xe bằng một hình cn màu trắng
    lcd.display();
}; // đóng xóa xe tăng

byte doi_huong(byte huong)
{
    // quy tắc đổi hướng: Quay sang bên trái của hướng hiện tại
    switch (huong) {
    case 1:
        return 2;
        break;
    case 2:
        return 3;
        break;
    case 3:
        return 4;
        break;
    case 4:
        return 1;
        break;
    default:
        break;
    } //dóng switch
}; // đóng đổi hướng

void ve_xe_dich_main(byte i)
{

    byte chon_huong_tank, phat_hien, x_tank, y_tank, huong_tank, ton_tai;
    byte buoc_tank;
    byte x_dan, y_dan, huong_dan, ton_tai_dan;
    //duyệt xe địch từ thứ 1 đến tank_dich_max trong mảng
    ton_tai = tank[i][0];
    if (ton_tai == 0) {
        // nếu xe thứ i không có, hoặc chết rồi thì cần tạo xe mới
        lcd.Number_Long(Xmin + i * ((Xmax - Xmin) / tank_dich_max), Ymin / 8, i, ASCII_NUMBER, BLACK);
        // viết 1 chữ số ra màn hình/ (Ymin/8) là dòng thứ ?
        ve_tank_dich(Xmin + i * ((Xmax - Xmin) / tank_dich_max), Ymin + 10, i % 4 + 1);
        // vẽ xe có tọa độ theo biển số xe
        //i%4+1, nếu i=4 hoặc i=8 suy ra i%4=0,
        //mà hướng 1,2,3,4, nên phải +1 để khác 0

        tank[i][1] = Xmin + i * ((Xmax - Xmin) / tank_dich_max); //ghi vào mảng hoành độ khởi đầu
        tank[i][2] = Ymin + 10; // ghi vào mảng tung độ khởi đầu
        tank[i][3] = (i % 4 + 1); //ghi vào mảng hướng ban đầu
        tank[i][4] = 0; // xe chưa đi 1 "bước" nào cả
        tank[i][0] = 1; // tích vào bảng, rằng đã tồn tại xe i
    }
    else { // xe i này vẫn sống
        //lấy thông tin của xe
        x_tank = tank[i][1];
        y_tank = tank[i][2];
        huong_tank = tank[i][3];
        buoc_tank = tank[i][4];
        phat_hien = kiem_tra_vat_can(x_tank, y_tank, huong_tank);
        // đổi hướng hay không

        if ((x_tank <= Xmin) || (x_tank >= Xmax) || (y_tank <= Ymin) || (y_tank >= Ymax) || (phat_hien == 1)) {

            // nếu ở trên biên hoặc có vật cản thì phải quay về hướng ngược lại
            tank[i][3] = doi_huong(huong_tank); // ghi vào mảng
        }

        //di chuyển
        huong_tank = tank[i][3]; // Rồi lại gán
        //sau đó nhích về phía trước 1 pixel
        nhich_ve_phia_truoc_dich(x_tank, y_tank, huong_tank, i);
        tank[i][4] = buoc_tank + 1; // nhích về phía trước thì phải cộng thêm 1 "bước"
        buoc_tank = tank[i][4]; //rồi lại gán, muốn dùng thì phải gán
        //CHuẩn bị bắn
        // QUY ƯỚC:   xe tăng thứ i sẽ di chuyển i*5 bước rồi bắn 1 lần
        // VÀ
        //QUY ƯỚC: xe chỉ được bắn khi viên đạn của xe đó đã bị mất
        ton_tai_dan = tank[i][8];
        if ((buoc_tank >= (i * 5)) && (ton_tai_dan == 0)) { //kiểm tra thỏa mãn
            //nạp đạn
            tank[i][7] = huong_tank; // đặt hướng đạn là hướng hiện tại của xe
            tank[i][5] = x_tank + 2; // viên đạn nằm bên trong xe
            tank[i][6] = y_tank + 2; // viên đạn nằm bên trong xe
            tank[i][4] = 0; // đặt lại bước = 0 (mất 1 ngày để tìm ra lỗi này, hic ^^)s
            tank[i][8] = 1; // tích vào mảng rằng viên đạn của xe i đã tồn tại
        }
        //vẽ đạn
        // lại gán
        if (ton_tai_dan == 1) {
            x_dan = tank[i][5];
            y_dan = tank[i][6];
            huong_dan = tank[i][7];
            //rồi mới vẽ
            ve_dan(x_dan, y_dan, huong_dan, i);

        } //đóng vẽ đạn
    } //đóng else if đầu

} // đóng vẽ xe địch main
void ve_xe_ta_main()
{

    /*
// DI CHUYỂN XE TA------------------------------------------------------
*/
    byte huong_tank_ta, x_tank_ta, y_tank_ta; //tank của ta
    byte huong_tank_ta_button;
    huong_tank_ta_button = lcd.pullup_4(right_b, up_b, left_b, down_b); // đọc giá trị diều khiển

    //lấy thông tin của xe tank
    x_tank_ta = tank[0][1];
    y_tank_ta = tank[0][2];
    huong_tank_ta = tank[0][3];
    /*buoc_tank=tank[i][4];
*/
    if (huong_tank_ta_button != 0) { // có 1 nút được nhấn
        if (huong_tank_ta_button != huong_tank_ta) {
            // nếu hướng cũ của xe khác với giá trị nút nhấn
            //thì đặt lại hướng của xe là giá trị nút nhấn
            //thế mới gọi là điều khiển chứ !
            tank[0][3] = huong_tank_ta_button;
            // gán
            huong_tank_ta = tank[0][3];
            // rồi vẽ
            ve_tank_ta(x_tank_ta, y_tank_ta, huong_tank_ta);
        }
        else {
            // ngược lại, nút ấn có vai trò di chuyển xe

            // nếu xe trong biên và không có vật cản thì mới được phép đi
            nhich_ve_phia_truoc_ta(x_tank_ta, y_tank_ta, huong_tank_ta);

        } // đóng else if ngoài
    } //dóng if ngoài
    /*
// TA BẮN ĐẠN-----------------------------------------------------
*/
    //đạn của ta
    byte huong_dan_ta, x_dan_ta, y_dan_ta, ton_tai_dan_ta;
    byte OK_BAN;

    //QUY ƯỚC: xe chỉ được bắn khi viên đạn của xe đã bị mất + NHẤN NÚT

    ton_tai_dan_ta = tank[0][8];
    OK_BAN = digitalRead(fight_b); // nút fight có được nhấn hay không
    if ((OK_BAN == 0) && (ton_tai_dan_ta == 0)) {
        //NÚT fight đã nhấn

        //lấy thông tin của xe tank
        x_tank_ta = tank[0][1];
        y_tank_ta = tank[0][2];
        huong_tank_ta = tank[0][3];
        //nạp đạn
        tank[0][7] = huong_tank_ta; // đặt hướng đạn là hướng hiện tại của xe
        tank[0][5] = x_tank_ta + 2; // viên đạn nằm bên trong xe
        tank[0][6] = y_tank_ta + 2; // viên đạn nằm bên trong xe
        // nạp đạn xong
        tank[0][8] = 1; // viên đạn đã tồn tại
    } // đóng if bắn
    //gán
    ton_tai_dan_ta = tank[0][8];
    //vẽ đạn
    if (ton_tai_dan_ta == 1) {

        //lấy thông tin
        x_dan_ta = tank[0][5];
        y_dan_ta = tank[0][6];
        huong_dan_ta = tank[0][7];
        //vẽ đạn
        ve_dan(x_dan_ta, y_dan_ta, huong_dan_ta, 0); // số "0" ở cuối là biển số của xe ta
    } // đóng if vẽ

} //đóng vẽ xe ta main
void bang_diem(byte i)
{

    lcd.Asc_String(10, 2, Asc("Diem:"), BLACK);
    lcd.Number_Long(35, 2, diem, ASCII_NUMBER, BLACK);

    lcd.Asc_String(10, 12, Asc("Thu pham:T"), BLACK);

    lcd.Number_Long(75, 12, i, ASCII_NUMBER, BLACK);
    lcd.display();

    //Reset tank[1+tank_dich_max][10]
    for (byte xxx = 0; xxx <= tank_dich_max; xxx++) {
        for (byte yyy = 0; yyy <= 10; yyy++) {
            tank[xxx][yyy] = 0;
        }
    }
    // reset diem
    diem = 0;

} // dong bang diem

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, DELETE);
        lcd.display();

        if (digitalRead(fight_b) == 0) {
            break; // thoát ngay
        }
        delay(250);
    }
} //đóng lặp vô hạn

/*====================================================
* ===================================================
* ĐÓNG KHAI BÁO CÁC HÀM+CÀI ĐẶT 
* ===================================================
* VÀO VÒNG LẶP CỦA DÒNG CHẢY CHÍNH
* ===================================================
* ===================================================
*/

void loop()

{
    //goi doi tuong la game

    //Bắt đầu game, vẽ  xe ta----------------------------------------

    byte xe_ta_ton_tai;
    xe_ta_ton_tai = tank[0][0];
    if (xe_ta_ton_tai == 0) {
        ve_tank_ta(30, 55, 2); // tại 30-55, đang hướng lên phía trên
        ve_nen(); //vẽ nền game
        tank[0][1] = 30; //ghi vào mảng hoành độ khởi đầu
        tank[0][2] = 55; // ghi vào mảng tung độ khởi đầu
        tank[0][3] = 2; //ghi vào mảng hướng ban đầu
        tank[0][4] = 0; // xe chưa đi 1 "bước" nào cả !
        tank[0][0] = 1; // tích vào bảng, rằng đã tồn tại xe ta
    }
    /*
//VẼ XE ĐỊCH----------------------------------------------------------
*/

    for (byte i = 1; i <= tank_dich_max; i++) {

        //duyệt xe địch từ thứ 1 đến  tank_dich_max trong mảng
        ve_xe_dich_main(i);
        //rồi lặp cho đủ tank_dich_max xe
    } // đóng for

    /*
//VẼ XE TA----------------------------------------------------------
*/
    ve_xe_ta_main();

    /*
// Xóa ĐẠN ĐỊCH VÀ TA
// NẾU ĐẠN VƯỢT BIÊN THÌ ĐẠN BỊ XÓA-----------------------
*/
    for (byte i = 0; i <= tank_dich_max; i++) {
        byte X_DAN, Y_DAN;
        X_DAN = tank[i][5];
        Y_DAN = tank[i][6];
        if ((X_DAN <= Xmin) || (X_DAN >= Xmax) || (Y_DAN <= Ymin) || (Y_DAN >= Ymax)) {
            tank[i][8] = 0; // xóa tồn tại đạn
            xoa_dan(X_DAN, Y_DAN);
            // xóa tọa độ đạn
            //không cần đâu, nó sẽ được ghi đè
            //và bảo vệ bởi hàm kiểm tra tồn tại
            // nếu quá 224 thì nó bị quay về 0 nếu ta tiếp tục tăng giá trị cho nó
        }

    } //đóng for xóa đạn

    /*
//KIỂM TRA VA CHẠM-------------------------------------------
*/
    // xét (tank_dich_max) xe địch và 1 viên đạn của ta
    //-------------------------------------------------
    int hieu_x_dich, hieu_y_dich;
    for (byte i = 1; i <= tank_dich_max; i++) {
        hieu_x_dich = tank[0][5] - tank[i][1];
        hieu_y_dich = tank[0][6] - tank[i][2];
        // nếu tọa độ đạn ta trừ tọa độ xe địch mà dương và <=5 và đạn còn tồn tại
        // dính đạn
        if ((hieu_x_dich >= 0) && (hieu_x_dich <= 5) && (hieu_y_dich >= 0) && (hieu_y_dich <= 5) && (tank[0][8] == 1)) {
            // vẽ một vụ nổ tại tọa độ xe đó
            no(tank[i][1], tank[i][2]);
            // khai tử xe tăng đó
            tank[i][0] = 0;
            // khai tử viên đạn cửa ta
            tank[0][8] = 0;
            //cộng điểm người chơi đạt được khi giết tank i
            diem++;
        } // đóng if
    } // đóng for

    // xét ta và (tank_dich_max) viên đạn cửa địch
    //--------------------------------------------------
    for (byte i = 1; i <= tank_dich_max; i++) {

        int hieu_x_ta, hieu_y_ta;
        // nếu tọa độ đạn địch trừ tọa độ xe ta mà dương và <=5
        // dính đạn
        hieu_x_ta = tank[i][5] - tank[0][1];
        hieu_y_ta = tank[i][6] - tank[0][2];

        if ((hieu_x_ta >= 0) && (hieu_x_ta <= 5) && (hieu_y_ta >= 0) && (hieu_y_ta <= 5)) {
            // vẽ một vụ nổ tại tọa độ xe đó
            no(tank[0][1], tank[0][2]);
            delay(1000);

            lcd.clear();

            //THUA CUỘC
            // BẢNG ĐIỂM
            ///---------------------------------------------

            bang_diem(i);

            //LẶP CÓ ĐIỀU KIỆN
            //Nhấn nút fight để chơi tiếp

            lap_vo_han();
            //-------------------------------------------

            //thoát lặp
            lcd.clear();

            goto the_end; // nhảy cóc tới nhãn the end cuối trang
            //nếu không có lệnh này, for sẽ lặp lại cho đến max con chạy
            //và ta sẽ phải nhấn giữ khá lâu
        } // đóng if

    } //đóng for kiểm tra va trạm

/*

// KẾT THÚC HÀM
// KẾT THÚC 2 NGÀY LÀM VIỆC CHĂM CHỈ!
// kiểm tra mảng
//___________________________
//__________________________
Serial.print("DIEM CUA BAN :");

Serial.println(diem);
int aaa,bbb;

Serial.println("___________YES/NO______X TANK____Y TANK__HUONG ____BUOC____X DAN____Y DAN_____HUONG_____YES/NO");
// XE TĂNG SỐ 0 LÀ CỦA BẠN 
// 1,2,3,4 LÀ ĐỊCH
// YES/NO : CÒN SỐNG HAY ĐÃ CHẾT
// X, Y TANK: TỌA ĐỘ XE TĂNG ĐÓ
// HUONG: HƯỚNG CỦA MŨI XE ĐÓ CHỈ ĐI ĐÂU (1->4) (ĐÔNG, BẮC, TÂY, NAM)
// BUOC; XE ĐÓ ĐÃ ĐI ĐƯỢC BAO NHIÊU BƯỚC (RIÊNG XE MÌNH THÌ KHÔNG CẦN)
// X,Y DAN: TỌA ĐỌ VIÊN ĐẠN CỦA XE TĂNG ĐÓ
// HƯỚNG VIÊN ĐẠN
// YES/NO: VIÊN ĐẠN CÒN HAY MẤT
for(aaa=0; aaa<tank_dich_max; aaa++){
Serial.print("XE SO:");
Serial.print(aaa);
for(bbb=0; bbb<10; bbb++){
Serial.print("________");
Serial.print(tank[aaa][bbb]);
} 
Serial.println("");
} 
// */

//Serial.println( getMemoryFree()); //KIỂM TRA RAM

the_end:

    //Serial.println( getMemoryFree()); //KIỂM TRA RAM
    delay(delay_toc_do);
} //đóng loop
int getMemoryFree()
{
    //HÀM NÀY TRẢ VỀ SỐ RAM CÒN DƯ TRÊN ARDUINO
    extern int __heap_start;
    extern int* __brkval;
    return (int)SP - (__brkval == 0 ? (int)&__heap_start : (int)__brkval);
    //mấy cái __heap_start, __brkval là những thanh ghi của cấu trúc harvard (cấu trúc của các vđk). VXL như intel, arm,.. dùng cấu trúc john von neuman
}

//////////
/////////
/////////

 

Cài map

Bạn có thể thay thế hàm ve_nen() bằng các hàm sau để tạo Map.

Map matrix

void ve_nen()
{
    //||||||||||||||||||||||||||||||||||||||||||
    //Bạn có thể vẽ bất cứ thứ gì lên màn hình khi nó
    //start game vào đây
    for (int x = 0; x < Xmax; x += 10) {
        for (int y = 15; y < 60; y += 10) {
            lcd.FillRect(x, y, 5, 5, BLACK);
            lcd.display();
            delay(30);
        }
    }
}

Map thành phố - Random

void ve_nen()
{
    //||||||||||||||||||||||||||||||||||||||||||
    //Bạn có thể vẽ bất cứ thứ gì lên màn hình khi nó
    //start game vào đây
    int x, y, size_brick;
    for (int i = 0; i < 50; i++) {
        // 50 viên gạch
        randomSeed(millis()); // lấy random từ đồng hồ
        x = random(Xmin, Xmax);
        y = random(Ymin, Ymax - 15);
        size_brick = random(3, 10);
        lcd.FillRect(x, y, size_brick, size_brick, BLACK);
        lcd.display();
        delay(30);
    }
}

Map rừng rậm - Random

void ve_nen()
{

    //||||||||||||||||||||||||||||||||||||||||||
    //Bạn có thể vẽ bất cứ thứ gì lên màn hình khi nó
    //start game vào đây

    int x, y, R;
    for (int i = 0; i < 25; i++) {
        // 50 viên gạch
        randomSeed(millis()); // lấy random từ đồng hồ
        x = random(Xmin, Xmax);
        y = random(Ymin, Ymax);
        R = random(3, 10);
        lcd.FillCircle(x, y, R, BLACK);
        lcd.display();
        delay(30);
    }
}

Tạm kết

Đây là file zip Game:

https://drive.google.com/file/d/0BzMEcyRK_uUFNFY0YzRSb1hyT0U/view?usp=sharing (mirror)

Hehe, làm Game vui phết các bạn ạ, chúc các bạn học C/C++ vui vẻ.

laughyesheartenlightenedenlightenedenlightened

Thái Sơn

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

Select any filter and click on Apply to see results

Các bài viết cùng tác giả

ST7565 | Chuyển động trong lập trình Game và đồ họa | Phần 3

Ở hai bài trước, chúng ta đã làm quen và biết cách điều khiển độc lập 1 đối tượng. ở phần 3 này, các bạn sẽ biết thêm về cách quản lý nhiều hơn 1 đối tượng trong game. Hãy cùng bắt đầu nào.laugh

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

ST7565 | Chuyển động trong lập trình Game và đồ họa | Phần 2

Trong bài viết trước, chúng ta đã cùng tìm hiểu một vài nguyên tắc của hiệu ứng chuyển động trong đồ họa. Bài viết này sẽ nối tiếp nội dung còn dở dang của bài trước, hãy cùng đi tiếp nào. Tất nhiên là trên arduino cùng lcd st7565 rồi.cheekyblush

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