❝ Just a silly blocky game ❞ — @qninhdt
Cybrion là một trò chơi 3D được viết bằng ngôn ngữ C++ phiên bản 20 và sử dụng thư viện SDL2 để xử lý đồ họa. Trong Cybrion, người chơi sẽ được thỏa thích xây dựng bất cứ thứ gì bằng những khối có sẵn, trong một thế giới mở tuyệt đẹp, không bị giới hạn bởi không gian.
- Thế giới mở, rộng vô hạn
- Nhiều loại khối như gỗ, đất, đá, xi măng. Nhiều loại hoa và loại cây
- Sử dụng đa luồng để sinh thế giới, giúp tăng hiệu suất trò chơi
- Có thể lưu và mở thế giới để chia sẻ với bạn bè
- Giao diện, đồ họa bắt mắt, dễ dùng
- Bước 1: Nhập tên thế giới muốn tạo rồi ấn nút
Create
- Bước 2: Nhấn nút
Play
ở thế giới vừa tạo - Bước 3: Enjoy the game 🗿🍷
Chuột trái
: phá khốiChuột phải
: đặt khốiWASD
: di chuyểnSpace
: bay lênShift
: hạ xuốngE
: mở túi đồ và chọn khốiEsc
: tạm dừngCtrl
: di chuyển nhanh hơnF1
: ẩn GUIF3
: mở công cụ debug
- Loading . . .
Thêm CMake vào biến môi trường (environment variables)
- Bước 1: Mở cửa sổ cmd rồi tải mã nguồn từ Github
> git clone https://github.com/qninhdt/cybrion
- Bước 2: Tải vcpkg và thư viện
> cd cybrion
> build.bat
- Step 3: Chạy lại file
build.bat
để biên dịch
> build.bat
Sau khi biên dịch, các file cần thiết cho game sẽ xuất hiện trong thư mục cybrion/build
Chạy file cybrion.exe
trong thư mục trên để chơi game
- Bước 1: Tải mã nguồn từ Github
$ git clone https://github.com/qninhdt/cybrion
- Bước 2: Chạy
build.sh
để tự động tải thư viện, phần mềm cần thiết và biên dịch mã nguồn
$ cd cybrion
$ ./build.sh
Tương tự như trên Windows thư mục chưa game là cybrion/build
├── resources # Chứa texture của game, file cấu hình, file shader, ...
└── src # Mã nguồn
| ├── client # Chứa mọi thứ liên quan đến cửa sổ, điều khiển, render
| | ├── GL # Xử lý OpenGL (mesh, framebuffer, texture, ...)
| | ├── graphic # Xử lý render khối, thế giới, bầu trời
| | ├── resource # Đọc file cấu hình, file shader, tải texture, ...
| | └── ui # Xử lý giao diện (inventory, menu, text, ...)
| ├── core # Chứa một vài cấu trúc dữ liệu quan trọng
| ├── physic # Xử lý vật lý (AABB)
| ├── uitl # Một số hàm hỗ trợ
| ├── world # Quản lý thế giới, khối và người chơi
| └── ...
└── ...
- Thế giới trong Cybrion được chia thành các chunk. Mỗi chunk có cấu tạo từ 32x32x32 khối. Thế giới sẽ lưu trữ các chunk dưới dạng map với key là 1 vector 3D gồm tọa độ x, y, z của chunk. (Tương tự như trong Minecraft, mỗi chunk có kích cỡ 16x256x16)
- Nhờ việc lưu trữ chunk bằng map thay vì mảng cố định hay vector mà thế giới có thể rộng vô hạn.
- Mỗi chunk chỉ được tạo hoặc đọc từ bộ nhớ khi cách người chơi một khoảng nhất định.
- Sử dụng thuật toán Perlin Noise để sinh thế :
- Dựa vào tọa độ và một seed ( seed được tạo ngẫu nhiên từ khi tạo thế giới ) để tạo ra một ảnh noise (hình 1)
- Từ ảnh noise, trò chơi sẽ sinh ra height map để phục vụ cho việc sinh thế giới (hình 2)
- Tiếp theo ta cũng có thể sử dụng các yếu tố khác như độ cao để lựa chọn khối để đặt
- Đọc thêm: Making maps with noise functions (redblobgames.com)
- Để thuận tiện cho quá trình lưu trữ thì mỗi block được cấp phát một ID duy nhất có giá trị nguyên dương. Ví dụ: id(AIR) == 0, id(DIRT) == 5, id(SAND) == 17, ...
-
Thế giới yêu cầu bộ nhớ quá lớn
-
Mặc dù mỗi block có thể lưu trữ dưới dạng 1 số nguyên tuy nhiên số lượng block trong thế giới là vô cùng lớn
-
Với tầm nhìn là 8 (mặc định của trò chơi) thì số chunk cần lưu trữ ≈ 1700 chunk = 1700 x 32 x 32 x 32 = 55,705,600 block
-
Nếu trò chơi của ta có hơn 256 loại block thì ta cần 2 byte cho mỗi block => bộ nhớ cần dùng ≈ 100Mb
-
Vậy chỉ sau 10 phút di chuyển, khám phá trò chơi thì bộ nhớ cần dùng để lưu trữ thế giới có thể lên tới vài Gb (uy tín)
-
-
Tốn quá nhiều thời gian để render
- Tương tự như vấn đề trên, việc có quá nhiều khối tương đương với có quá nhiều vật thể cần được render.
- Số block cần vẽ là 55,705,600 block, mỗi block lại có 6 mặt => số hình chữ nhật cần vẽ là 334,233,600 hình !
-
Không có cách lưu trữ phù hợp:
- File text, json: Tốn bộ nhớ, tốc độ đọc rất kém
- Database: không phù hợp, quá phức tạp
-
Sử dụng cấu trúc dữ liệu Palette:
-
Do trong mỗi chunk chủ yếu chỉ có từ 3 - 10 loại block khác nhau nên việc sử dụng 2 byte tương ứng với 65536 giá trị khác nhau là rất lãng phí.
-
Palette là một cấu trúc dữ liệu tự động điều chỉnh bộ nhớ tùy thuộc vào số block khác nhau trong chunk, tận dụng triệt để từng bit trong một byte để lưu trữ
Số block khác nhau trong 1 chunk Tỉ lệ Số bit mỗi block Kích cỡ chunk 1 - 2 Ít 1 4Kb 3 - 4 Rất nhiều 2 8Kb 5 - 16 Nhiều 4 16Kb 17 - 256 Hiếm 8 32Kb 257 - 65536 Không có 16 64Kb
-
-
Sử dụng file JBT để lưu trữ thế giới:
- JBT hay "JSON-like binary tag" là một kiểu file lưu trữ dữ liệu dưới dạng nhị phân được mình phát triển nhằm mục đích vừa tận dụng sự dễ hiểu của json mà vừa có thể lưu trữ được dữ liệu lớn
- JBT sẽ lưu chunk thành 1 region file có đuôi .hjbt. Một region sẽ chứa tối đa 32x32x32 chunk, tuy nhiên file này chỉ lưu trữ những chunk đã được sinh ra
- Ngoài ra JBT còn sử dụng thuật toán lz4 để nén dữ liệu trước khi lưu
-
Chỉ render những block có thể nhìn thấy:
- Ta ẩn những block mà 6 mặt xung quanh đều có block vì nó chắc chắn không thế nhìn thấy
- Nhờ cách này mà ta có thể giảm đáng kể số block phải render từ O(n³) xuống O(n²)
- SDL2 - Xử lý màn hình và điều khiển
- glad- Xử lý đồ họa
- irrKlang - Xử lý âm thanh
- glm - Tính toán ma trận và vector
- FastNoiseLite - Tạo height map để sinh thế giới
- imgui - Tạo giao diện người dùng
- spdlog - Logging
- yaml-cpp - Đọc file yaml
- thread-pool - Xử lý đa luồng
- concurrentqueue - Thread-safe queue
- Texture pack: Jolicraft
- Sound: : Minecraft
- Font: Minecraft