Logic của game trong njin được chia thành ba khái niệm:
| Khái niệm | Là gì | Kiểu |
| System | Một hàm chạy ở một thời điểm nhất định trong frame | njin::sys_fnc |
| Phase | Một giai đoạn của frame mà system được gắn vào | njin::sys_phase |
| Module | Một nhóm system có tên, đăng ký cùng lúc | njin::mod_desc |
System
System là một hàm thường, nhận context của engine và không trả về gì:
void move(njin::context &ctx);
Nó không có trạng thái riêng. Dữ liệu nằm trong component của entity (xem Entity, component và event) hoặc trong biến của file.
Phase
Mỗi frame gọi các phase theo thứ tự cố định. Xem chi tiết ở Vòng lặp game.
Module
Module là nơi bạn khai báo system. Nó gồm một cái tên và một hàm setup:
#include <njin.h>
namespace {
void spawn(njin::context &ctx) {
const entt::entity entity = registry.create();
}
void setup(njin::context &ctx) {
}
}
return {.name = "hello", .setup = setup};
}
int main() {
.width = 800.0f,
.height = 600.0f,
.target_fps = 60.0f};
return 0;
}
void run(context &ctx)
Chạy vòng lặp chính cho đến khi cửa sổ đóng.
context * create(const config &cfg)
Mở cửa sổ và trả về context của engine.
void destroy(context *ctx)
Giải phóng mọi tài nguyên của engine và đóng cửa sổ.
#define NJIN_INFO(...)
Ghi log mức info. Tham số giống printf.
Definition njin_log.h:98
void ecs_register(context &ctx, sys_phase phase, sys_fnc fnc, const char *name=nullptr)
Thêm một system vào lịch chạy của một phase.
void mod_register(context &ctx, const mod_desc &desc)
Chạy setup của module và đưa các system của nó vào lịch chạy.
entt::registry & world(context &ctx)
Trả về registry của EnTT chứa mọi entity và component của game.
@ phase_startup
Một lần, trước frame đầu tiên.
Definition _mod.h:32
Cấu hình cửa sổ và vòng lặp, truyền vào create().
Definition njin_cfg.h:11
Mô tả một module: một nhóm system có tên.
Definition _mod.h:67
Cách hoạt động:
- mod_register() gọi setup đúng một lần.
- Trong setup, bạn gọi ecs_register() cho từng system để gắn nó vào một phase.
- Từ đó, system chạy mỗi khi phase của nó chạy.
Các quy tắc:
- mod_register() phải được gọi trước run().
- Tên module phải duy nhất. Đăng ký hai lần cùng một tên bị bỏ qua và ghi cảnh báo.
- ecs_register() chỉ hợp lệ bên trong setup. Gọi ở nơi khác bị bỏ qua và ghi cảnh báo.
- Module lõi của engine (camera, âm thanh, sprite) đã được đăng ký sẵn bởi create().
Đăng ký nhiều module một lần
Thay vì gọi mod_register() cho từng module, truyền cả danh sách:
Danh sách tạo lúc chạy (std::vector<njin::mod_desc>, std::array) cũng được:
std::vector<njin::mod_desc> mods{input_module(), physics_module()};
if (debug)
mods.push_back(debug_overlay_module());
Kết quả giống hệt gọi từng module lần lượt: module đứng trước chạy trước trong cùng phase, và một module lỗi (ví dụ trùng tên) chỉ bị bỏ qua riêng nó, các module còn lại vẫn được đăng ký.
Thứ tự chạy
Có hai tầng:
- Giữa các module: chạy theo thứ tự đăng ký. Module đăng ký trước, system của nó chạy trước, trong cùng một phase.
- Trong một module, trong một phase: theo ràng buộc bạn khai báo bằng njin::sys_desc.
Nếu không khai báo gì, các system chạy theo đúng thứ tự bạn đăng ký. Khi cần kiểm soát:
#include <njin.h>
namespace {
void read_input(njin::context &) {}
void move(njin::context &) {}
void clamp_to_screen(njin::context &) {}
void debug_overlay(njin::context &) {}
void setup(njin::context &ctx) {
}
}
return {.name = "physics", .setup = setup};
}
@ phase_update
Mỗi frame, logic chính của game.
Definition _mod.h:35
Mô tả một system kèm ràng buộc thứ tự, dùng với ecs_register().
Definition _mod.h:47
| Trường | Ý nghĩa |
| after | Các system phải chạy trước system này |
| before | Các system phải chạy sau system này |
| order | Giá trị nhỏ chạy trước, trong số các system đã được after/before cho phép (mặc định 100) |
| scene | Chỉ chạy khi scene này đang chạy. Để trống là chạy ở mọi scene (xem Scene) |
Khi hai system cùng sẵn sàng và có cùng order, system đăng ký trước chạy trước.
- Lưu ý
- after/before chỉ tham chiếu được các system cùng module và cùng phase. Tham chiếu tới system ở nơi khác bị bỏ qua và ghi cảnh báo. Nếu các ràng buộc tạo thành vòng (A sau B, B sau A), engine ghi lỗi và để các system còn lại chạy theo thứ tự đăng ký.
Ví dụ hoàn chỉnh: chuyển động
Một component velocity do game tự định nghĩa, và một system cộng vận tốc vào vị trí:
#include <njin.h>
namespace {
struct velocity {
};
void spawn(njin::context &ctx) {
const entt::entity entity = registry.create();
registry.emplace<velocity>(entity, velocity{.value = {50.0f, 0.0f}});
}
void move(njin::context &ctx) {
for (auto [entity, tr, vel] : view.each()) {
tr.pos.x += vel.value.x * dt;
tr.pos.y += vel.value.y * dt;
}
}
void setup(njin::context &ctx) {
}
}
return {.name = "movement", .setup = setup};
}
const char * tr(const context &ctx, const char *key)
Chuỗi đã dịch của key trong ngôn ngữ đang dùng.
f32 delta(const context &ctx)
Thời gian của frame trước, tính bằng giây, đã nhân với tốc độ thời gian.
float f32
Số thực 32 bit.
Definition _types.h:28
Vector 2 chiều. Dùng cho vị trí, kích thước, độ dời.
Definition _types.h:39
Lưu ý dòng njin::delta(ctx): nhân vận tốc với thời gian của frame để tốc độ không phụ thuộc FPS.