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

Mô tả dự án: 

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

Stop_Motion

Đây là bài viết số 2 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 viết trước, thì hãy đọc nó tại đây bạn nhé:

P1:Chuyển động trong lập trình game và đồ họa

Hãy cùng bắt đầu nhélaughwink

Chúng ta đã biết, mỗi khung hình cho một đối tượng ở một thời điểm là khác nhau (nếu giống nhau thì ta coi vật đó là tĩnh hay đứng im , nên ta sẽ không xét), vì vậy để hành động của đối tượng được mền mại, không quá cứng nhắc thì việc duy trì hành động với quỹ đạo theo thời gian càng mượt mà càng tốt.

Để làm được điều này, người lập trình lại phải quan tâm đến những chi tiết nhỏ hơn.

Đây cũng là nguyên tắc Arcs -Bất cứ một chuyển động nào cũng cần tạo ra đường cong của chuyển động từ điểm bắt đầu tới kết thúc.

"Ngày nay, với sự hỗ trợ của máy tính và phần mềm, trong lập trình game có đòi hỏi chất lượng tốt, thì nguyên tắc trên sẽ được khai khác một cách hiệu quả nhất. Phương pháp tối ưu nhất hiện nay để quản lý và điều khiển hình ảnh của đối tượng ảo là việc sử dụng một bản đồ các điểm nút , mà ở đó tập hợp các điểm sẽ cho khung của đối tượng , và việc tô màu đối tượng ảo sẽ là việc tô trên các mạng lưới nhỏ hơn cấu thành từ các điểm..'".

Tuy nhiên nguyên tắc này vẫn có thể bị phá vỡ dần khi người ta muốn cải thiện tốc độ và các thay đổi của chuyển động sẽ ở một mức chấp nhận được, giống như các tựa game đơn giản trên nền Mobi cấu hình thấp. Và điều này cũng phù hợp khi ta mong muốn tạo game trên Arduino.

Dưới đây là các stop_motion khi các động tác của nhân vật được tổng quát với những bản phác ở các góc độ khác nhau,nó hướng tới sự đơn giản hóa, điều này sẽ làm tăng đáng kể hiệu xuất của máy tính .

Stop_Motion trên arduino .

Chúng ta sẽ cùng quay trở lại với nhân vật Penguin mập ú cùng với sự hỗ trợ của Arduino và lcd ST7565

Đầu tiên là bản phác họa các hành động

Chuyển sang dạng ảnh bmp và lấy mã hex lưu trữ của ảnh

Để lấy mã hex, mình sử dụng ứng dụng java lấy trong thư viện của OPEN GLCD

  • Trong file tải xuống tìm đến file glcdMakeBitmap.jad rồi mở nó
  • Mở song song thư mục chứa file BMP của bạn.
  • Nhấn giữ rồi kéo thả ảnh xuống cái cửa sổ chương trình glcdMakeBitmap.jad trên
  • File hex sẽ nằm ngay ở thư mục lớn có chứa glcdMakeBitmap
  • Mở file hex bằng NotePad , rồi copy đoạn nằm trong ngoặc { }, chú ý sửa lại ..
  • //ví dụ  

    25, // width   26, // height

    //viết thành

     // 25, width   //26,  height

  • Dán vào code mẫu của bạn là xong. Bạn cũng cần biết sử dụng hàm vẽ ảnh Bitmap trong thư viện tải về nhé.
  • Nếu có lỗi mở file .jad, thì bạn cần thiết lập máy tính có hỗ trợ môi trường JAVA, hãy lên google tham khảo hoặc sử dụng phần mềm khác thay thế.(Mình đã thử khá nhiều phần mềm, nhưng thấy ứng dụng java trên không ăn bớt mã hex của mình khi biên dịch yesheart, hãy cố gắng thiết lập môi trường java cho máy tính của bạn  nha)

Tải ứng dụng tại đây.

Để có ảnh dưới tập tin BMP, hãy tham khảo bài viết của tác giả LeQuocChi nhé http://arduino.vn/result/1308-ve-anh-bitmap-tren-oled-i2c-rat-dep-ban-cam-nhan-thu-xem

Quay lại với chủ đề nào wink

 walk1  walk2  walk3  walk4
 jump1  jump2  jump3  
 slide1  slide2  hurt  
 die1  die2 die3  

Mình đã chuyển ảnh bitmap sang dạng code lưu trữ trong một tệp có tên bmp1.h

Tải về tại đây

Tư liệu đã sẵn sàng, cùng mở IDE lên và tạo mới một chương trình thôi!!smiley

Chọn New Tab, và đặt tên file là “bmp1.h”, sau đó copy toàn bộ code trong file tải về dán vào, rồi nhớ ấn lưu.

 

Cuối cùng là thêm tệp  "bmp1.h"

Ta có code sau để test như sau

#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6); 
#include "bmp1.h"//
void setup() {
    lcd.ON();
    lcd.SET(23,0,0,0,4);
}
void loop() {
    lcd.bitmap(60,30,25,28,walk1,BLACK);
    lcd.display();
}

Kết quả khi test bằng đoạn code trên

Mô phỏng hành động

Bước đi

#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
#include "bmp1.h"
void setup()   {  
    lcd.ON();
    lcd.SET(23,0,0,0,4);
}


//Tạo một hàm có tên walk,với  3 tham số là x,y: tọa độ ; time_d: delay.
void walk(int x, int y,unsigned int time_d){
    //p1
    lcd.Bitmap( x,y,25,28,walk1,BLACK); 
    lcd.Display();
    delay(time_d);
    lcd.fillrect(x,y,25,28,DELETE);
    //p2
    lcd.Bitmap( x+3,y,25,28,walk2,BLACK); 
    lcd.Display();
    delay(time_d);
    lcd.fillrect(x+3,y,25,28,DELETE);
    //p3
    lcd.Bitmap( x+6,y,25,28,walk3,BLACK);
    lcd.Display();
    delay(time_d); 
    lcd.fillrect(x+6,y,25,28,DELETE);
    //p4
    lcd.Bitmap( x+9,y,25,28,walk4,BLACK); 
    lcd.Display();
    delay(time_d);
    lcd.fillrect(x+9,20,25,28,DELETE);
    lcd.Display();
}


void loop(){
    for(int x=0; x<100; x+=9){
        walk(x,35,200);
    }
}

 

Như bài trước, ta đã biết cách thức để vẽ một khung hình là một quy trình gồm các bước: tính toán->hiển thị->đợi->xóa.

Quy trình sẽ lặp lại với những khung hình tiếp theo. Một bước đi của nhân vật Penguin gồm 4 khung hình, do đó quy trình vẽ sẽ lặp lại  4 lần.  Tất nhiên ta cần dùng đến hàm for để nhân bản hành động khi tăng dần hoành độ , tạo ra hành động đi bộ. 

Tối ưu hóa code

Bất cứ dòng lệnh nào có tính lặp lại nhiều lần ta đều cần tối ưu nó.

Cụ thể là 4 khối code trên đã lặp lại, và mình sẽ cho vào hàm action để thực hiện nhiệm vụ chuyên biệt này.

#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
#include "bmp1.h"
void setup()   {  
    lcd.ON();
    lcd.SET(23,0,0,0,4);
}
void action( int x, int y, int width, int high, const char* bitmap_name, unsigned  int time_d){
    
    lcd.Bitmap( x,y,width,high,bitmap_name,BLACK); 
    lcd.Display();
    delay(time_d);
    lcd.fillrect(x,y,width,high,DELETE);
    lcd.Display();
}
void walk(int x, int y, unsigned int time_d){

    action(x,y,25,28,walk1, time_d);
    action(x+3,y,25,28,walk2, time_d);
    action(x+6,y,25,28,walk3, time_d);
    action(x+9,y,25,28,walk4, time_d);

}
void loop() {
    for(int x=0; x<100; x+=9)
        walk(x,35,250);
}

Kết quả vẫn như trước

Giờ thì ta cần quay ảnh ngược lại ra phía sau nếu muốn Penguin đi từ phải sang trái

Ta cần đến Plus_Bitmap để xoay ảnh. Chúng ta cũng cần quy ước về hướng, hướng tổng quát sẽ được hiểu là góc hợp bởi vector chỉ hướng và trục hoành. 

Kể từ đây, khi nói  hướng   0’:Trái ,180’:Phải,90’: Lên,270’: Xuống Hoặc 45:  chếch phải. và hiểu theo nghĩa ngược lại .

Để quay hướng nhân vật ra phía sau, mình sẽ lật ảnh bằng gương

Cùng với đó là định nghĩa thêm 4 hướng.

#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6);
#include "bmp1.h"// 


#define Phai 0
#define Len 90
#define Trai 180
#define Xuong 270
void setup()   {  
    lcd.ON();
    lcd.SET(23,0,0,0,4);
}


void action( int x, int y, int width, int high, const char* bitmap_name, unsigned  int time_delay,unsigned  int huong){
    bool mirror=false;
    if(huong==Trai){
        mirror=true;// nếu đi sang trái thì xoay nhân vật về bên trái
    }
    lcd.Plus_Bitmap( x,y,width,high,bitmap_name,0,mirror,BLACK); 
    lcd.Display();
    delay(time_delay);
    lcd.fillrect(x,y,width,high,DELETE);
    lcd.Display();
}

void walk(int x, int y, unsigned int time_delay,unsigned int huong){
    int denta=1;//  sang phải thì x tăng
    if(huong==Trai){
        denta=-1;// sang trái thì x giảm, denta phải âm
    }
    action(x,y,25,28,walk1, time_delay,huong);
    action(x+3*denta,y,25,28,walk2, time_delay,huong);
    action(x+6*denta,y,25,28,walk3, time_delay,huong);
    action(x+9*denta,y,25,28,walk4, time_delay,huong);
}

void loop(){
    byte y0=35;
    
    for(int x=30; x<60; x+=9)
        walk(x,y0,220,Phai);  
    
    for(int x=60; x>30; x-=9)
        walk(x,y0,220,Trai);  
}

Kết quả, nhân vật đi qua đi lại như thế này.

Tương tác

Tiếp tục dùng nút bấm để điều khiển nhân vật Penguin nào

 

Bạn hãy tham khảo cách nối dây tại bài viết trước nhé:

http://arduino.vn/tutorial/1319-st7565-huong-dan-su-dung-glcd-st7565-homephone-va-chia-se-thu-vien

Để thuận tiện trong nạp code, mình sẽ không thay đổi thứ tự nút và chân kết nối, nên bạn hãy nối giống mình nhé.

Bây giờ để điều khiển, chỉ đơn giản mình khai báo thêm 4 dòng ở Setup và dùng hàm kiểm tra sự kiện nhấn nút Pullup_4.

Đầu tiên là điều khiển đối tượng đi qua đi lại

#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6); 
#define  de 100
#define Phai 0
#define Tren 90
#define Trai 180
#define Duoi 270


#include "bmp1.h"// chú ý thêm
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); 
}



void action( int x, int y, int width, int high, const char* bitmap_name, unsigned  int time_delay,unsigned  int huong){
    bool mirror=false;
    if(huong==Trai){
        mirror=true;// nếu đi sang trái thì xoay nhân vật về bên trái
    }
    lcd.Display();
    lcd.Plus_Bitmap( x,y,width,high,bitmap_name,0,mirror,BLACK); 
    lcd.Display();
    delay(time_delay);
    lcd.fillrect(x,y,width,high,DELETE);
}

void walk(int x, int y, unsigned int time_delay,unsigned int huong){
    int denta=1;//  sang phải thì x tăng
    if(huong==Trai){
        denta=-1;// sang trái thì x giảm, denta phải âm
    }
    action(x,y,25,28,walk1, time_delay,huong);
    action(x+3*denta,y,25,28,walk2, time_delay,huong);
    action(x+6*denta,y,25,28,walk3, time_delay,huong);
    action(x+9*denta,y,25,28,walk4, time_delay,huong);

}

void stand(int x, int y,unsigned int huong){
    bool mirror=false;
    if(huong==Trai){
        mirror=true;// nếu đi sang trái thì xoay nhân vật về bên trái
    }
    lcd.Plus_Bitmap( x,y,25,28,walk2,0,mirror,BLACK);
    lcd.display(); 
    delay(10);
    lcd.fillrect(x,y,25,28,DELETE);
}

int x=60;//hoành độ khảo sát
int y=30;// tung độ khảo sát
byte button;
int huong;// góc xoay ảnh

void loop(){
    button= lcd.Pullup_4(A3,  A2, A1, A0);
    switch(button){
        case 1:     huong=Phai;walk(x,y,200,huong); x+=9;  break;// right 
        case 3:     huong=Trai;walk(x,y,200,huong); x-=9; break;//left
        default : stand(x,y,huong); break;
    } 
}

Và cuối cùng thêm 6 động tác là Nằm-nút down, nhảy cao-nút up+(left/right),Trượt_nút down+(left/right), và Nhảy tại chỗ nút up

#include "ST7565_homephone.h"
ST7565 lcd(3,4,5,6); 
#define  de 100
#define Phai 0
#define Tren 90
#define Trai 180
#define Duoi 270


#include "bmp1.h"// chú ý thêm
void setup()   {   
    lcd.ON();
    lcd.SET(23,0,0,0,4);
    //thêm hai dòng này khi cần dùng đến màn hình
    
    pinMode(A3,INPUT_PULLUP); 
    pinMode(A2,INPUT_PULLUP); 
    pinMode(A1,INPUT_PULLUP);       
    pinMode(A0,INPUT_PULLUP); 
}


void action( int x, int y, int width, int high, const char* bitmap_name, unsigned  int time_delay,unsigned  int huong){
    bool mirror=false;
    if(huong==Trai){
        mirror=true;// nếu đi sang trái thì xoay nhân vật về bên trái
    
    }
    lcd.Display();
    lcd.Plus_Bitmap( x,y,width,high,bitmap_name,0,mirror,BLACK); 
    lcd.Display();
    delay(time_delay);
    lcd.fillrect(x,y,width,high,DELETE);
}

void walk(int x, int y, unsigned int time_delay,unsigned int huong){
    int denta=1;//  sang phải thì x tăng
    if(huong==Trai){
        denta=-1;// sang trái thì x giảm, denta phải âm
    }
    action(x,y,25,28,walk1, time_delay,huong);
    action(x+3*denta,y,25,28,walk2, time_delay,huong);
    action(x+6*denta,y,25,28,walk3, time_delay,huong);
    action(x+9*denta,y,25,28,walk4, time_delay,huong);

}

void stand(int x, int y,unsigned int huong){
    bool mirror=false;
    if(huong==Trai){
        mirror=true;// nếu đi sang trái thì xoay nhân vật về bên trái
    }
    lcd.Plus_Bitmap( x,y,25,28,walk2,0,mirror,BLACK);
    lcd.display(); 
    delay(10);
    lcd.fillrect(x,y,25,28,DELETE);
}


void lie(int x, int y, unsigned int time_delay,unsigned int huong){
    bool mirror=false;
    if(huong==Trai){
        mirror=true;// nếu đi sang trái thì xoay nhân vật về bên trái
    }
    //action(x,y+10,25,22,slide1,time_delay,huong);
    action(x,y+8,30,19,slide2,time_delay,huong);

}

void die( int x, int y, unsigned int time_delay,unsigned int huong){
    action(x,y,25,26,die1, time_delay,huong);
    action(x,y+3,25,27,die2, time_delay,huong);
    action(x,y+6,30,23,die3,time_delay,huong); 
    delay(500);
}

void jump(int x, int y, unsigned int time_delay,unsigned int huong){
    int denta=1;//  sang phải thì x tăng
    if(huong==Trai){
        denta=-1;// sang trái thì x giảm, denta phải âm
    }
    action(x,y-8,25,27,jump1, time_delay,huong);
    action(x,y-5,24,24,jump2, time_delay,huong);
    action(x,y-3,24,24,jump3, time_delay,huong);

}
void high_jump(int x0, int y, unsigned int time_delay,unsigned int huong){
    int denta=1;//  sang phải thì x tăng
    if(huong==Trai){
        denta=-1;// sang trái thì x giảm, denta phải âm
    }
    for(int denta_x=0; denta_x<10; denta_x+=2){
        y-=2;
        action(x0+denta_x*denta,y,24,24,jump2, time_delay,huong);
    }
    x0+=10*denta;
    for(int denta_x=0; denta_x<10; denta_x+=2){
        y+=2;
        action(x0+denta_x*denta,y,24,24,jump3, time_delay,huong);
    }

}



int x=60;//hoành độ khảo sát
int y=30;// tung độ khảo sát
byte button;
byte trangthai;
int huong;// góc xoay ảnh
void loop(){
    button= lcd.Pullup_4(A3,  A2, A1, A0);
    switch(button){
        case 1:     huong=Phai;walk(x,y,250,huong); x+=9;  break;// right 
        case 2:     jump(x,y,150,huong);  break;// up
        case 3:     huong=Trai;walk(x,y,250,huong); x-=9; break;//left
        case 4:  lie(x,y,300,huong); break; //down
        case 40:  huong=Phai; x++; lie(x,y,5,huong); break;// slide right
        case 120: huong=Trai; x--; lie(x,y,5,huong); break;// slide left
        case 60: huong=Trai; high_jump(x,y,100,huong); x-=20;break;// jump higher
        case 20: huong=Phai; high_jump(x,y,100,huong);x+=20;break;// jump higher
        
        default : stand(x,y,huong); break;
    } 
    if(x<0){x=0;}
    if(y<0){y=0;}
    if(x>100){x=100;}
    if(y>53){y=53;}


}

 

Vẫn còn hai động tác là Die và Hurt, cái này mình dành ra để cho các bạn sáng tạo

MORE ...

Code đã dài, và vẫn còn nhiều cái hay với chủ đề này, mình xin tạm dừng tại đây, hi vọng bạn cảm thấy thích bài viết của mình. yesenlightened

Mình xin chia sẻ một trang web có tài nguyên đồ họa 2d khá hay untamed.wild-refuge.net  

 Còn nhiều cái hay hơn nữa..laughlaughcheeky Hẹn gặp lại các bạn ở bài tiếp theo.

Tác giả:  Thái Sơn.

lên
10 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

Bộ điều khiển PID - ứng dụng phần 2 - xe dò line dùng thuật toán PID

Tiép nối bài viết về xe dò line cảm ơn Đỗ Hữu Toàn đã viết hộ mình phần 4. hôm nay mình sẽ làm cho chiếc xe dò line đi mượt và có hồn hơn 

lên
34 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ả

Giới thiệu module ESP32 và hướng dẫn cài trình biên dịch trên Arduino Ide.

Hiện tại , module wifi esp8266 đã có mặt ở khắp nơi , nhà nhà dùng ESP8266. Vậy đâu là sự lựa chọn tiếp theo sau ESP8266 ? Câu trả lời từ nhà sản xuất ESP (espressif.com) đó là  :   “ESP32”

Cấu hình khủng, thêm chức năng , tăng số chân I/O, thêm nhiều cảm biến , giá thành phù hợp…là những gì mình sẽ giới thiệu về esp32 tại bài viết này.

lên
12 thành viên đã đánh giá bài viết này hữu ích.
Từ khóa: 

Nâng 128kbyte SRAM ngoài cho arduino với IC 23lc1024

Ở bài trước, mình đã hướng dẫn các bạn nâng cấp 32kbyte SRAM cho arduino mega. Còn có 1 loại SRAM cực nhỏ gọn đang được ưa chuộng là 23lc1024 với dung lượng 128kbyte. Bài này mình sẽ hướng dẫn các bạn sử dụng nó với arduino. Đi nào...

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