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

Mô tả dự án: 

Ở 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

MULTI OBJECT

Đây là bài 3 thuộc chuỗi bài "Chuyển động trong lập trình game và đồ họa", nếu chưa đọc bài trước thì bạn hãy đọc nó trước khi tiếp tục nhé. 

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

  • Trong bài sử dụng bảng lớn, phù hợp hơn với màn hình máy tính khi xem, bạn hãy xoay ngang màn hình nếu đang dùng điện thoại.
  • Bài viết sử dụng arduino uno r3 và lcd ST7565 cùng bộ thư viện st7565_homephone.

Cùng bắt đầu lại với ví dụ thần thánh basic nhất trong chuyển động: Cho một đối tượng di chuyển trên màn hình.

#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(){
  for( byte x=0; x<127; x++){
    lcd.fillcircle( x,10,3,BLACK);
    lcd.display();// cho phép hiển thị vẽ
    delay(50);
    lcd.fillcircle( x,10,3,DELETE); 
    lcd.display();// cho phép hiển thị xóa
  }
}

Ví dụ trên là cho đối tượng A (hình tròn, r=3px,) chạy từ trái sang phải.

Yêu cầu là bây giờ vẽ thêm 1 đối tượng B  chạy song song với đối tượng A.

 

Vậy thì hàm loop  sẽ như thế này:

void loop(){
    for( byte x=0; x<127; x++){
        
        lcd.fillcircle( x,10,3,BLACK);    
        lcd.fillcircle( x,20,3,BLACK);    
        lcd.display();// cho phép hiển thị vẽ
        delay(50);
        lcd.fillcircle( x,10,3,DELETE);
        lcd.fillcircle( x,20,3,DELETE); 
        lcd.display();// cho phép hiển thị xóa
    }
}

Thêm 4 đối tượng C,D,E,F nữa cùng chạy song song với A,B thì code sẽ như thế nào ??smiley

Dưới đây là code cho ví dụ trên.

void loop(){
  for( byte x=0; x<127; x++){
    lcd.fillcircle( x,10,3,BLACK);    
    lcd.fillcircle( x,20,3,BLACK);    
    lcd.fillcircle( x,30,3,BLACK);    
    lcd.fillcircle( x,40,3,BLACK);    
    lcd.fillcircle( x,50,3,BLACK);  
    lcd.fillcircle( x,60,3,BLACK);
    lcd.display();// cho phép hiển thị vẽ
    delay(50);
    lcd.fillcircle( x,10,3,DELETE);
    lcd.fillcircle( x,20,3,DELETE);
    lcd.fillcircle( x,30,3,DELETE);
    lcd.fillcircle( x,40,3,DELETE);
    lcd.fillcircle( x,50,3,DELETE);
    lcd.fillcircle( x,60,3,DELETE); 
    lcd.display();// cho phép hiển thị xóa
  }
}

Chúng ta rút ra vài điều như sau:

  • Có bao nhiêu đối tượng trong game thì có bấy  nhiêu đối tượng bạn cần phải quản lý.
  • Thời gian để hoàn thiện một khung ảnh tỉ lệ thuận với số lượng đối tượng
  • Tài nguyên dung lượng bị thiếu hụt ít hay nhiều phụ thuộc vào số lượng đối tượng và mức độ phức tạp trong thuộc tính của đối tượng .

Ta cần làm gì..??

Quay lại với đoạn code trên, nếu cứ giữ nguyên giải thuạt mà lại yêu cầu vẽ 10 đối tượng hoặc hơn thế chạy song song từ trái qua phải thì chúng ta cần có cách quản lý phù hợp hơn.

Có nhiều cách, bảng thống kê là một lựa chọn đơn giản mà chúng ta thường gặp.

Ví dụ, cô giáo của bé khi sử dụng phần mềm LaChildren, sẽ theo dõi và quản lý trẻ thông qua một bảng thống kê mọi thông tin về trẻ.

(nguồn ảnh internet)

Cùng so sánh cách quản lý của cô giáo và máy tính:

 Trong lớp học

Trong lập trình game

Các bé.

Các đối tượng trong game.

Cô giáo là người quản lý các bé.

Máy tính quản lý các đối tượng game.

Mỗi bé có một tên để phân biệt.

(bỏ qua trường hợp giống tên)

Mỗi đối tượng cũng cần có gì đó để phân biệt:

Chỉ số index, name-id,..

Quản lý  bé dựa trên các yếu tố  giống nhau nhất:

Năm sinh, Lớp , Chiều cao, cân nặng..

Quản lý  đối tượng dựa trên các thuộc tính giống nhau nhất:

Tọa độ, kích thước, màu sắc,..

Cô giáo quản lý bằng bảng.

Máy tính cũng quản lý bằng “bảng”.

Vậy “bảng “ của máy tính là gì, nếu bạn chưa biết, thì mình xin giới thiệu , đó là mảng 2 chiều.

Ví dụ đầu tiên về sử dụng mảng 2 chiều :

Bắt đầu hay rổi đây, cùng mở IDE của bạn lên và trải nghiệm về mảng 2 chiều thôi.!!

void setup(){
  Serial.begin(9600);
}
void loop(){
}

Nếu bạn chưa quen với khái niệm mảng 2 chiều, khi mình viết là “bảng” thì bạn chỉ cần tưởng tượng tới cái bảng này là được. 

Đầu tiên,khai báo “bảng” có kích thước 3x4 có kiểu dữ byte , tức là khai báo 3x4=12 biến  có kiểu dữ liệu byte.

Bước

 Code

Trạng thái các biến

Khai báo

 


 void setup(){
  Serial.begin(9600);
}
 byte Array[3][4];
void loop(){
}

 

Ghi dữ liệu

void setup(){
  Serial.begin(9600);
}
byte Array[3][4];
void loop(){
Array[0][0]=88;
Array[2][3]=99;
}

 

Lấy(kiểm tra) dữ liệu

void setup(){
  Serial.begin(9600);
}
byte Array[3][4];
void loop(){
Array[0][0]=88;
Array[2][3]=99;
Serial.print(Array[0][0]);
Serial.print("|");
Serial.println(Array[2][3]);
}

 

Ghi đè(thay đổi) dữ liệu.

void setup(){
Serial.begin(9600);
}
byte Array[3][4];
void loop(){
//ghi
Array[0][0]=88;
Array[2][3]=99;
//ghi đè
Array[0][0]=77;
Array[2][3]=55;
//kiểm tra lại
Serial.print(Array[0][0]);
Serial.print("|");
Serial.println(Array[2][3]);
}

 

Rất dễ phải không bạn!^^.

Trước khi tiếp tục, bạn hãy dành ra ít phút để check lại  các đoạn code vừa rồi, mạnh dạn sửa đổi code nhé.

…………………………………….

……………………………………

Vậy còn gì mà mình chưa nói về mảng hai chiều nhỉ?

Hihi, hết rồi. Nó đơn giản là mảng các biến thui. Từ giờ mình dùng thuật ngữ mảng 2 chiều được rồi.

Cùng quay lại với yêu cầu quản lý 6 đối tượng ABCDEF bằng mảng 2 chiều

Chúng ta xác định 2 thuộc tính chung của các đối tượng là hoành độ x, tung độ y.


#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
void setup()   {  
lcd.ON();
lcd.SET(23,0,0,0,4);
}
int object[6][2];
void loop(){
}

 


#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
void setup()   {  
lcd.ON();
lcd.SET(23,0,0,0,4);
}
int object[6][2];
void loop(){
//ghi dữ liệu tung độ y
object[0][1]=10;
object[1][1]=20;
object[2][1]=30;
object[3][1]=40;
object[4][1]=50;
object[5][1]=60;
}

 

 


#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
void setup()   {  
lcd.ON();
lcd.SET(23,0,0,0,4);
}
int object[6][2];
void loop(){
//ghi dữ liệu tung độ y
for( int i=0; i<6; i++){
  object[i][1]=10*(i+1);
}

 //ghi dữ liệu hoành độ x
for( int i=0; i<6; i++){
object[i][0]=30;

}

}

 

 


#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
void setup()   {  
lcd.ON();
lcd.SET(23,0,0,0,4);
}
int object[6][2];
void loop(){
//ghi dữ liệu tung độ y
for( int i=0; i<6; i++){
 object[i][1]=10*(i+1);
}
// vẽ chuyển động
for( int x=0; x<127; x++){

    //ghi dữ liệu hoành độ và vẽ

     for( int i=0; i<6; i++){
      object[i][0]=x;
     lcd.fillcircle(  object[i][0] ,  object[i][1],3,BLACK);
     }
     lcd.display();
    // đợi
    delay(50);
    // xóa ảnh
     for( int i=0; i<6; i++){
       lcd.fillcircle(  object[i][0] ,  object[i][1],3,DELETE);
     }
  }
}

 

 

Kết quả 

Control_object

Bây giờ mình sẽ điều khiển biệt lập đối tượng F , còn các đối tượng ABCDE để cho máy tính đảm nhiệm.

#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
void setup()   {   
    lcd.ON();
    lcd.SET(23,0,0,0,4);
    pinMode(A3,INPUT_PULLUP); 
    pinMode(A2,INPUT_PULLUP); 
    pinMode(A1,INPUT_PULLUP);       
    pinMode(A0,INPUT_PULLUP); 
}
int object[6][2];
byte button;
void loop(){
    //ghi dữ liệu tung độ y
    for( int i=0; i<6; i++){
        object[i][1]=10*(i+1);
    }
    // vẽ chuyển động
    for( int x=0; x<127; x++){
        //ghi dữ liệu hoành độ cho ABCDE
        for( int i=0; i<5; i++){
            object[i][0]=x; 
        }
        // ghi dữ liệu cho đối tượng F
        button= lcd.Pullup_4(A3,  A2, A1, A0);
        switch(button){
            case 1:     object[5][0]++;break;// right 
            case 2:     object[5][1]--;  break;// up
            case 3:     object[5][0]--;  break;//left
            case 4:      object[5][1]++; break; //down
            default : break;
        } 
    
    
        for( int i=0; i<6; i++){//vẽ ABCDEF
            lcd.fillcircle(  object[i][0] ,  object[i][1],3,BLACK);
        }
        lcd.display();
        delay(50);
        for( int i=0; i<6; i++){
            lcd.fillcircle(  object[i][0] ,  object[i][1],3,DELETE);
        }
    }
}

Mình đoán là đến đây, bạn cũng đã mường tượng được một vài vấn đề trong game rùi đấy.

OK, đó là cách quản lý thứ nhất :sử dụng mảng 2 chiều.

Cách quản lý hay hơn ??

Cách hai là dùng kiểu dữ liệu tự định nghĩa. Class

Cũng giống với ví dụ trên, mình  chưa đưa ra định nghĩa Class, vì  nó .. nếu chỉ mang mình nó để mà định nghĩa thì sẽ khiến bạn bối rối .

Trên tinh thần, “yêu” trước , “yêu’  rồi mới định nghĩa được “tình yêu”.

Thì chúng ta cần đi vào ví dụ,:

Chủ đề của ví dụ là quản lý đối tượng bằng mảng 2 chiều và class.

Mảng 2 chiều

Class

 

#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
void setup()   {  
Serial.begin(9600);
}
int circle[2][2];
void loop(){
}

Khai báo mảng 2 chiều có 2 thuộc tính x,y (như ví dụ đã biết bên trên ha) quản lý 2 đối tượng .

Đối tượng A :circle[0][…];

Đối tượng B: circle[1][…];

 

#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
void setup()   {  
Serial.begin(9600);
}
class object{
public :
int x,y;
};
object circle[2];
void loop(){
}

Tạo một class để quản lý 2 đối tượng  , với  2 thuộc tính x,y.

Đối tượng A: circle[0];

Đối tượng B: circle[1];

 

 

#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
void setup()   {  
Serial.begin(9600);
}
int circle[2][2];
void loop(){
//ghi dữ liệu cho A
circle[0][0]=30;//xA
circle[0][1]=40;//yA
// đọc dữ liệu của A
Serial.print(circle[0][0]);
Serial.print("|");
Serial.println(circle[0][1]);
}

 

#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
void setup()   {  
Serial.begin(9600);
}
class object{
public :
int x,y;
};
object circle[2];
void loop(){
//ghi dữ liệu cho A
circle[0].x=30;//xA
circle[0].y=40;//yA
// đọc dữ liệu của A
Serial.print(circle[0].x);
Serial.print("|");
Serial.println(circle[0].y);
} 

 

 

 

Tạm dừng một chút và so sánh 2 cách trên, cá nhân mình thấy cách dùng class có cảm tình hơn nhiều, các bạn nghĩ sao?

Đúng hơn là nó dễ biểu đạt về mặt thuộc tính. 

Và đây là đoạn code sử dụng class thay cho mảng 2 chiều:

Bây giờ mình sẽ điều khiển biệt lập đối tượng F , còn các đối tượng ABCDE để cho máy tính đảm nhiệm.

#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
void setup()   {   
  lcd.ON();
  lcd.SET(23,0,0,0,4);
  
  pinMode(A3,INPUT_PULLUP); 
  pinMode(A2,INPUT_PULLUP); 
  pinMode(A1,INPUT_PULLUP);       
  pinMode(A0,INPUT_PULLUP); 
}

class object{
  public :
  int x,y;
};
object circle[6];
byte button;
void loop(){
  //ghi dữ liệu tung độ y
  for( int i=0; i<6; i++){
    circle[i].y=10*(i+1);
  }
  // vẽ chuyển động
  for( int x=0; x<127; x++){
 
    //ghi dữ liệu hoành độ cho ABCDE
    for( int i=0; i<5; i++){
      circle[i].x=x;  
    }
    // ghi dữ liệu cho đối tượng F
    button= lcd.Pullup_4(A3,  A2, A1, A0);
  
    switch(button){
      case 1:     circle[5].x++;break;// right 
      case 2:     circle[5].y--;  break;// up
      case 3:     circle[5].x--;  break;//left
      case 4:      circle[5].y++; break; //down
      default : break;
    } 


   
     
    for( int i=0; i<6; i++){//vẽ ABCDEF
     lcd.fillcircle(  circle[i].x ,  circle[i].y,3,BLACK);
    }
    lcd.display();
    // đợi
    delay(50);
    // xóa ảnh
    for( int i=0; i<6; i++){
      lcd.fillcircle(  circle[i].x ,  circle[i].y,3,DELETE);
    }
  }
}

Kết quả vẫn như vậy.

Tạm kết.

Vậy đó, nhờ đam mê làm game mà chúng ta lại biết được vài thứ mới của arduino.

Nếu cả mảng 2 chiều, class là kiến thức mới đối với bạn.

Sau bài viết này có 2 điều mà bạn nên và không nên làm:

  • Bạn nên tìm hiểu về mảng 2 chiều.
  • Không nên vội vã ngấu nghiến các tài liệu về class. Về phần này, cứ để mình dẫn dắt bạn cùng với những ví dụ trực quan nhất, mọi thứ về class sẽ được mở ra dần thôi.

Hi vọng bạn thấy thích bài viết nàylaugh

Ở bài tiếp theo, chúng ta sẽ đổi gió một chút, đảm bảo bạn thấy thích thú.

<Thái Sơn>

lên
7 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 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
7 thành viên đã đánh giá bài viết này hữu ích.

Điều khiển pin bằng ngôn ngữ chính thống

Xin chào các bạn, việc điều khiển nhập/ xuất trên arduino bằng digitalRead/ Write hẳn đã quá quen thuộc . Hài lòng với tốc độ hiện có, vậy bạn có muốn tăng tốc độ đọc/ ghi lên gấp 14 lần,  điều khiển cả 8 pin cùng lúc chỉ với một dòng lệnh không. Hãy đọc ngay bài viết này.

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