Nick Chung gửi vào
- 13617 lượt xem
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
.
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
- 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
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 :
- Phần 1: Hiệu ứng đồ họa Game trên lcd
- Phần 2: Chuyển động và lập trình cơ bản
- Phần 3: Quản lí các đối tượng Game
- Phần 4: Xử lí va chạm của của các đố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ẻ.

![]()






