njin
Engine 2D gọn nhẹ để tự tay làm game theo ý mình. Vibe coding cũng ổn luôn
Loading...
Searching...
No Matches
Camera

Camera trong njin là một entity có ba component. Không có đối tượng camera riêng.

Component Vai trò
njin::transform pos là điểm trong thế giới mà camera nhìn vào, rot là góc xoay. scale bị bỏ qua
njin::camera_2d offset là vị trí trên màn hình nơi pos được vẽ, zoom là độ phóng đại
njin::camera_on Tag đánh dấu đây là camera đang dùng

Tạo camera

#include <njin.h>
namespace {
// Tag do game tự định nghĩa để tìm người chơi.
struct player_tag {};
void spawn(njin::context &ctx) {
entt::registry &registry = njin::world(ctx);
const njin::vec2 screen = njin::screen_size(ctx);
const entt::entity player = registry.create();
registry.emplace<njin::transform>(player);
registry.emplace<player_tag>(player);
// Camera là một entity có ba component: transform, camera_2d, camera_on.
const entt::entity camera = registry.create();
registry.emplace<njin::transform>(camera);
registry.emplace<njin::camera_2d>(
camera, njin::camera_2d{.offset = {screen.x * 0.5f, screen.y * 0.5f},
.zoom = 2.0f});
registry.emplace<njin::camera_on>(camera);
}
// Đưa camera tới chỗ người chơi mỗi frame.
void follow_player(njin::context &ctx) {
entt::registry &registry = njin::world(ctx);
const auto players = registry.view<const njin::transform, const player_tag>();
const auto cameras = registry.view<njin::transform, const njin::camera_on>();
for (const entt::entity player : players) {
const njin::vec2 target = players.get<const njin::transform>(player).pos;
for (const entt::entity camera : cameras) {
cameras.get<njin::transform>(camera).pos = target;
}
}
}
void setup(njin::context &ctx) {
}
} // namespace
njin::mod_desc camera_demo_module() {
return {.name = "camera_demo", .setup = setup};
}
// Đổi tọa độ: điểm trong thế giới nằm ở đâu trên màn hình sau khi qua camera.
njin::vec2 world_to_screen_example(const njin::context &ctx,
njin::vec2 world_pos) {
return njin::w2scr(ctx, world_pos);
}
vec2 w2scr(const context &ctx, vec2 pos)
Đổi một điểm từ thế giới sang pixel màn hình, qua camera_active().
vec2 screen_size(const context &ctx)
Trả về kích thước màn hình mà game vẽ lên, tính bằng pixel.
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.
entt::registry & world(context &ctx)
Trả về registry của EnTT chứa mọi entity và component của game.
@ phase_post_update
Mỗi frame, sau khi cập nhật. Event được phát ngay sau phase này.
Definition _mod.h:36
@ phase_startup
Một lần, trước frame đầu tiên.
Definition _mod.h:32
2D camera.
Definition _comps.h:25
Tag đánh dấu camera đang được dùng để vẽ và cho w2scr()/scr2w().
Definition _comps.h:37
Mô tả một module: một nhóm system có tên.
Definition _mod.h:67
Vị trí, góc xoay và tỉ lệ của một entity trong thế giới.
Definition _comps.h:14
Vector 2 chiều. Dùng cho vị trí, kích thước, độ dời.
Definition _types.h:39
f32 y
Thành phần dọc (trong không gian màn hình, y tăng khi đi xuống).
Definition _types.h:41
f32 x
Thành phần ngang.
Definition _types.h:40

Trong ví dụ:

  • offset là nửa kích thước màn hình, nên điểm camera nhìn vào luôn nằm giữa màn hình.
  • zoom = 2 làm mọi thứ to gấp đôi. Giá trị <= 0 được coi là 1.
  • follow_player chạy ở phase_post_update, sau khi người chơi đã di chuyển, để camera không bị chậm một frame.

Đổi camera

Camera được đọc trực tiếp từ registry mỗi lần cần, nên sửa transform hay camera_2d là có hiệu lực ngay, không cần gọi hàm nào.

Muốn chuyển qua camera khác: bỏ njin::camera_on khỏi camera cũ và gắn vào camera mới.

Lưu ý
Nếu nhiều entity cùng có njin::camera_on, một trong số chúng được dùng nhưng bạn không kiểm soát được là cái nào. Chỉ giữ một camera có tag này.

Không có camera nào thì thế giới trùng với màn hình: tọa độ (0, 0) là góc trên trái.

Đổi tọa độ

Hàm Đổi từ Sang
njin::w2scr() Thế giới Pixel màn hình
njin::scr2w() Pixel màn hình Thế giới

Cả hai đi qua njin::camera_active(), là góc nhìn đang dùng (njin::camera_view).

Trường hợp thường gặp nhất là đổi vị trí chuột thành vị trí trong thế giới. njin::mouse_pos() trả về pixel màn hình, không qua camera:

const njin::vec2 target = njin::scr2w(ctx, njin::mouse_pos(ctx));
vec2 scr2w(const context &ctx, vec2 pos)
Đổi một điểm từ pixel màn hình sang thế giới, qua camera_active().
vec2 mouse_pos(const context &ctx)
Vị trí con trỏ chuột, tính bằng pixel màn hình.

Xem Nhập liệu.

njin::camera_bounds() trả về vùng thế giới đang hiện trên màn hình. Dùng để bỏ qua những thứ nằm ngoài màn hình; tilemap dùng nó để chỉ vẽ các chunk nhìn thấy.

Camera và các phase vẽ

Module camera của engine bật camera ở đầu phase_pre_render và tắt ở đầu phase_post_render:

  • Mọi thứ vẽ ở phase_pre_render và phase_render chịu ảnh hưởng của camera.
  • Mọi thứ vẽ ở phase_post_render không. Đây là chỗ đặt UI.

Xem Vòng lặp game.