Bài giảng Lập trình nâng cao: Danh sách liên kết cung cấp cho người học các kiến thức: Trò chơi Snake, kỹ thuật mảng 2 chiều, bắt phím với SDL_PollEvent, xử lý hiện tượng rớt phím, thêm, chèn, xóa trên danh sách hiệu quả.
Snake Game 13&14 - Danh sách liên kết Nội dung ● ● Trò chơi: Snake Kỹ thuật ○ ○ ○ Mảng chiều Bắt phím với SDL_PollEvent() Hàng đợi ■ ○ xử lý tượng rớt phím Danh sách liên kết ■ thêm, chèn, xoá danh sách hiệu Trị chơi Snake ● Sân chơi hình chữ nhật ○ ● Rắn lúc đầu ○ ● ● dài ô (tính đầu), hình, xuống Người chơi điều khiển rắn di chuyển phím mũi tên Mỗi lần rắn ăn cherry dài thêm ô ○ ● Trên sân chơi xuất cherry ngẫu nhiên Thử sức: nhiều loại quả, loại tác dụng Rắn va phải tường → thua ○ https://www.youtube.com/watch?v=kTIPpbIbkos Demo - Start Screen Demo - Midgame screen Các tác vụ trò chơi ● Hiển thị hình vẽ giới thiệu ○ ● ● Có nút hiển thị bảng xếp hạng lần chơi Khởi tạo: sân chơi, rắn, vị trí Game loop, bước: ○ ○ Xử lý kiện bàn phím để đổi hướng bước Xử lý game logic: di chuyển rắn theo hướng tại, va chạm tường, va chạm thân rắn, ăn dài thân tăng điểm số ○ Hiển thị hình trị chơi Lộ trình xây dựng trị chơi Các phiên 0.1: vẽ sân chơi rắn đơn giản (dùng vng hình trịn), điều khiển rắn di chuyển 0.2: thêm vào sân chơi, rắn ăn dài 0.3: xử lý va chạm với cạnh sân thân rắn 0.4: Vẽ đốt rắn đẹp ảnh JPG 1.0: Thêm hình khởi động, điểm số, bảng xếp hạng Chuẩn bị ● ● ● ● Tạo project Snake Cài đặt thư viện SDL2, SDL2_image Đưa main.cpp, painter.h, painter.cpp từ giảng SDL vào project Sửa main.cpp ○ ○ ○ Xoá hàm vẽ Sửa tiêu đề cửa sổ Chỉ để lại mã khởi tạo giải phóng SDL ■ cửa sổ bút vẽ Chuẩn bị Hàm main() in t m ain (in t argc,ch ar* argv[]) { srand(tim e(0)); SD L_W indow * w indow ; SD L_Renderer* renderer; initSD L(w indow ,renderer); Painter painter(w indow , renderer); // TO D O : gam e code here quitSD L(w indow ,renderer); retu rn 0; } Mã giả render splash screen; initialize play-ground size = (w idth, height) render play-ground (save tim estam p) w h ile (gam e is running) { get user input update snake direction using user input (turn up, dow n,left, right) if elapsed tim e > required delay betw een steps m ove the gam e (snake craw l,generate cherry) to the next step render play-ground save new tim estam p } render gam e-over screen update score and ranking table to fi le Phiên 0.4: vẽ đẹp ● Ở phiên trước để có chương trình nhanh ta chưa cấu trúc code vẽ, cần ○ ○ ○ ○ ● hàm vẽ đường ngang: drawHorizontalLine hàm vẽ đường dọc: drawVerticalLine hàm vẽ cherry: drawCherry hàm vẽ rắn: drawSnake Các hàm cần vẽ toạ độ tương điểm (left,top) ○ Để sau phải di chuyển khung vẽ Vẽ đường ngang, dọc vo id d raw V erticalLin e(Painter& painter, in t left, in t top, in t cells) { painter.setC olor(W H ITE_C O LO R ); painter.setAngle(-9 0); painter.setPosition(left,top); painter.m oveForw ard(cells * C ELL_SIZE); } vo id d raw H o rizo n talLin e(Painter& painter, in t left, in t top, in t cells) { painter.setC olor(W H ITE_C O LO R ); painter.setAngle(0 ); painter.setPosition(left,top); painter.m oveForw ard(cells * C ELL_SIZE); } Vẽ cherry, vẽ ô rắn void d raw C h erry(Painter& painter, in t left,in t top) { painter.setC olor(O RAN G E_C O LO R ); Sử dụng vector thay cho danh sách liên kết, painter.setAngle(-9 0); truyền trỏ head thi bên ngồi thay đổi vị trí painter.setPosition(left+ 5, top+ ); painter.createSquare(C ELL_SIZE-1 ); } void d raw S n ake(Painter& painter, in t left, in t top,vector< Position> positions) { painter.setC olor(R ED _C O LO R ); painter.setAngle(0); for (Position pos : positions) { painter.setPosition(left+ pos.x*C ELL_SIZE+ 5, top+ pos.y*C ELL_SIZE+ C ELL_SIZE/2); painter.createC ircle(C ELL_SIZE/2 -5); } } đốt rắn Lấy vị trí đốt rắn Dùng hàm const PlayGround để bảo vệ liệu rắn vector< Position> PlayG round::getSnakePositions() st { vector< Position> res; fo r (SnakeN ode* p = snake.getH ead(); p != nullptr; p = p-> next) res.push_back(p-> position); retu rn res; } vo id ren d erG am eP lay(Painter& painter, co n st PlayG round& playG round) { Hàm vẽ sau cấu trúc lại in t top = 0, left = 0; in t w idth = playG round.getW idth(), height = playG round.getH eight(); painter.clearW ithBgC olor(PU R PLE_C O LO R ); fo r (in t i= 0; i< = w idth; i+ + ) draw VerticalLine(painter,left+ i*C ELL_SIZE, top+ ,height); fo r (in t i= 0; i< = height; i+ + ) draw H orizontalLine(painter, left+ 0, top+ i* C ELL_SIZE, w idth); st vector< vector< C ellType> > & squares = playG round.getSquares(); fo r (in t i= 0; i< height; i+ + ) for (in t j= ; j< w idth; j+ + ) if (squares[i][j] = = C ELL_C H ER RY) draw C herry(painter,left+ j*C ELL_SIZE, top+ i*C ELL_SIZE); vector< Position> snakePositions = playG round.getSnakePositions(); draw Snake(painter, left, top,snakePositions); SD L_RenderPresent(painter.getRenderer()); } Vẽ cherry ● ● ● Chọn ảnh đẹp cho cherry Ghi vào đĩa thành file cherry.png Dùng Painter::loadTexture đọc ảnh ○ ● Dùng Painter::createImage vẽ ảnh ○ ● Đọc ảnh lần lúc chương trình khởi động Sửa hàm createImage phép vẽ ảnh vào hình chữ nhật cửa sổ Do có nhiều ảnh trị chơi, tạo lớp Gallery để quản lý ảnh Lớp Gallery en u m PictureID { PIC _C H ER RY = 0, }; class G allery { Danh sách std::vector< SD L_Texture*> pictures; Painter& painter; SDL_Texture chứa ảnh theo thứ tự PictureID p u b lic: G allery(Painter& painter_); ~ G allery(); Đọc ảnh theo thứ tự vo id load G am eP ictu res(); SD L_Texture* getIm age(PictureID id) st { retu rn pictures[id]; } }; Lớp Gallery G allery::G allery(Painter& painter_) : painter(painter_) { loadG am ePictures(); } G allery::~ G allery() { for (SD L_Texture* texture : pictures) SD L_D estroyTexture(texture); } void G allery::loadG am ePictures() { pictures.push_back(painter.loadTexture("cherry.png")); } Huỷ ảnh đọc huỷ đối tượng Gallery Sửa hàm Painter::createImage Thêm khả đưa ảnh vào vị trí cửa sổ b oo l createIm ag e( SD L_Texture* texture, SD L_Rect* srcrect = nullptr, SD L_Rect* dstrect = nullptr ); b oo l Painter::createIm age( SD L_Texture* texture, SD L_Rect* srcrect, SD L_Rect* dstrect) { if( texture = = N U LL ) retu rn false; SD L_RenderC opy( renderer,texture,srcrect, dstrect ); retu rn true; } Sử dụng Gallery vẽ cherry G allery* gallery = nullptr; // globalpicture m anager in t m ain (in t argc,ch ar* argv[]) { Painter painter(w indow ,renderer); gallery = n ew G allery(painter); d elete gallery; quitSD L(w indow ,renderer); } vo id d raw C h erry(Painter& painter, in t left,in t top) { SD L_Rect dst = { left+ ,top+ ,CELL_SIZE-1 0,C ELL_SIZ E-1 }; painter.createIm age(gallery-> getIm age(PIC _C H ERRY), N U LL,& dst); } Đến chương trình vẽ cherry đẹp từ file ảnh cherry.png Vẽ rắn ● ● ● Rắn gồm đốt đầu đốt thân Khi di chuyển, đốt thân nằm ngang dọc Vậy cần ảnh ○ ○ ○ Đầu Thân ngang Thân dọc Vẽ rắn Thêm ảnh en u m PictureID { PIC _C H ER RY = ,PIC _SN AKE_V ERTIC AL, PIC _SN A KE_H O R IZO N TAL, PIC _SN AKE_H EA D , }; void G allery::loadG am ePictures() { pictures.push_back(painter.loadTexture("cherry.png")); pictures.push_back(painter.loadTexture("snake_vertical.png")); pictures.push_back(painter.loadTexture("snake_horizontal.png")); pictures.push_back(painter.loadTexture("snake_head.jpg")); } Vẽ rắn vo id d raw S n ake(Painter& painter, in t left, in t top, vector< Position> pos) { fo r (size_t i= ; i< pos.size(); i+ + ) { SD L_Rect dst = { left+ pos[i].x*C ELL_SIZE+ ,top+ pos[i].y*C ELL_SIZE+ , C ELL_SIZE-2 ,C ELL_SIZE-2 }; SD L_Texture* texture = N U LL; if (i> ) { if (pos[i].y = = pos[i-1 ].y) texture = gallery-> getIm age(PIC _SN A KE_H O R IZO N TAL); else texture = gallery-> getIm age(PIC _SN A KE_VERTIC AL); } else { // snake's head texture = gallery-> getIm age(PIC _SN AKE_H EA D ); } painter.createIm age(texture, N U LL, & dst); } } Phiên 0.4 https://github.com/tqlong/advprogram/archive/691fb99d67b2 a5effcb8954141e5a0a812c1fbdf.zip Bài tập ● ● Thêm ảnh đốt góc Thêm loại khác ○ ○ ○ Cho phép xuyên tường Cho phép xuyên rắn Cho phép dài nhiều đốt ... Mảng chiều Bắt phím với SDL_PollEvent() Hàng đợi ■ ○ xử lý tượng rớt phím Danh sách liên kết ■ thêm, chèn, xoá danh sách hiệu Trị chơi Snake ● Sân chơi hình chữ nhật ○ ● Rắn lúc đầu ○ ● ● dài... lỗi ? Giải pháp: forward declaration http://stackoverflow.com/questions/4757565/what-are-forward-declarations-in-c ● Khai báo class PlayGround; trước khai báo lớp Snake #include “PlayGround.h” Snake.cpp... Snake::Snake(PlayG round* playG round) : position(playG round-> getW idth() / 2, playG round-> getH eight() / 2), th is-> playG round(playG round) { playG round-> changeC ellState(position, C ELL_SN AKE); }