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
Toán học, va chạm, số ngẫu nhiên

Các phần này chỉ gồm header, không phụ thuộc engine: dùng được cả ngoài system.

#include <njin.h>
#include <vector>
namespace {
struct bullet {
};
std::vector<bullet> bullets;
njin::rect enemy{{300, 100}, {40, 40}};
void shoot(njin::context &ctx) {
return;
const njin::vec2 from{100, 200};
const njin::vec2 target = njin::scr2w(ctx, njin::mouse_pos(ctx));
// Hướng tới chuột, lệch ngẫu nhiên tối đa 5 độ.
const njin::f32 spread = njin::random(ctx).range(-5.0f, 5.0f);
const njin::vec2 dir = njin::rotate(njin::normalize(target - from), spread);
bullets.push_back({from, dir * 400.0f});
}
void move(njin::context &ctx) {
const njin::f32 dt = njin::delta(ctx);
for (bullet &b : bullets) {
b.pos += b.vel * dt;
// Nảy khỏi kẻ địch: đẩy ra rồi phản xạ vận tốc.
const njin::contact hit = njin::collide_circle_rect({b.pos, 4}, enemy);
if (hit.hit) {
b.pos += hit.normal * hit.depth;
b.vel = njin::reflect(b.vel, hit.normal);
}
}
// Bỏ đạn đã bay xa.
std::erase_if(bullets, [](const bullet &b) {
return njin::length_sq(b.pos) > 2000.0f * 2000.0f;
});
}
void draw(njin::context &ctx) {
for (const bullet &b : bullets)
}
void setup(njin::context &ctx) {
}
} // namespace
njin::mod_desc shooter_module() { return {.name = "shooter", .setup = setup}; }
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().
contact collide_circle_rect(circle c, rect r)
Va chạm hình tròn với hình chữ nhật.
Definition _collide.h:122
constexpr vec2 reflect(vec2 velocity, vec2 normal)
Phản xạ vận tốc qua một pháp tuyến, như bóng nảy khỏi tường.
Definition _collide.h:158
void draw_circle(const context &ctx, vec2 center, f32 radius, rgba color)
Vẽ hình tròn đặc.
void draw_rect(const context &ctx, rect r, rgba color)
Vẽ hình chữ nhật đặc.
bool mouse_pressed(const context &ctx, mouse_button button)
Đúng ở frame mà nút chuột vừa được nhấn xuống (chỉ một frame).
vec2 mouse_pos(const context &ctx)
Vị trí con trỏ chuột, tính bằng pixel màn hình.
@ mouse_left
Chuột trái.
Definition _types.h:475
constexpr f32 length_sq(vec2 v)
Bình phương độ dài.
Definition _math.h:58
vec2 rotate(vec2 v, f32 degrees)
Xoay vector theo chiều kim đồng hồ trên màn hình (trục y hướng xuống).
Definition _math.h:83
vec2 normalize(vec2 v)
Vector cùng hướng, độ dài 1.
Definition _math.h:74
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.
@ phase_update
Mỗi frame, logic chính của game.
Definition _mod.h:35
@ phase_render
Mỗi frame, vẽ trong không gian thế giới.
Definition _mod.h:38
@ phase_fixed_update
Theo nhịp cố định, 0 hoặc nhiều lần mỗi frame. Cho vật lý.
Definition _mod.h:34
rng & random(context &ctx)
Bộ sinh số ngẫu nhiên dùng chung của engine.
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
constexpr rgba yellow
Vàng.
Definition _math.h:239
constexpr rgba red
Đỏ.
Definition _math.h:236
Kết quả của một phép va chạm có đẩy ra.
Definition _collide.h:12
vec2 normal
Hướng đẩy vật thứ nhất ra, độ dài 1.
Definition _collide.h:14
bool hit
Hai hình có chồng nhau không.
Definition _collide.h:13
f32 depth
Độ sâu chồng nhau theo hướng normal.
Definition _collide.h:15
Mô tả một module: một nhóm system có tên.
Definition _mod.h:67
Hình chữ nhật thẳng trục: pos là góc trên trái, size là rộng và cao.
Definition _math.h:13
f32 range(f32 lo, f32 hi)
Số thực trong [lo, hi).
Definition _random.h:50
Vector 2 chiều. Dùng cho vị trí, kích thước, độ dời.
Definition _types.h:39

vec2

njin::vec2 hỗ trợ đầy đủ phép toán:

njin::vec2 a{3, 4}, b{1, 2};
a + b; a - b; a * 2.0f; 2.0f * a; a / 2.0f; -a;
a * b; // nhân từng thành phần
a += b; a *= 0.5f;
a == b;
Hàm Trả về
njin::dot(), njin::cross() Tích vô hướng, tích có hướng 2D
njin::length(), njin::length_sq(), njin::distance() Độ dài, bình phương độ dài, khoảng cách
njin::normalize() Vector độ dài 1 ({0,0} giữ nguyên)
njin::rotate(), njin::from_angle(), njin::angle_of() Xoay, vector từ góc, góc của vector
njin::lerp(), njin::clamp(), njin::move_toward() Nội suy, giới hạn, tiến về đích

Góc tính bằng độ, theo chiều kim đồng hồ trên màn hình (vì trục y hướng xuống): 0 độ là sang phải, 90 độ là xuống dưới. Cùng quy ước với njin::transform::rot.

njin::rect (góc trên trái + kích thước) và njin::circle là hai hình cơ bản. njin::rect_from_center() và njin::rect_center() đổi qua lại với tâm.

Va chạm

Kiểm tra chồng nhau (đúng/sai): njin::point_in_rect(), njin::point_in_circle(), njin::rects_overlap(), njin::circles_overlap(), njin::circle_rect_overlap().

Va chạm có đẩy ra trả về njin::contact: njin::collide_rects(), njin::collide_circles(), njin::collide_circle_rect(). Khi hit đúng, dời vật thứ nhất một đoạn normal * depth thì hai vật vừa chạm mép:

const njin::contact hit = njin::collide_rects(player, wall);
if (hit.hit)
player.pos += hit.normal * hit.depth;
constexpr contact collide_rects(rect a, rect b)
Va chạm hai hình chữ nhật, đẩy a ra theo trục chồng ít nhất.
Definition _collide.h:90

njin::reflect() phản xạ vận tốc qua pháp tuyến: bóng nảy khỏi tường.

Ghi chú
Chỉ chạm mép thì không tính là chồng nhau. Nhờ vậy vật vừa được đẩy ra không bị coi là vẫn còn va chạm ở frame sau.

Số ngẫu nhiên

njin::random() trả về bộ sinh dùng chung của engine, gieo hạt giống từ thời gian lúc mở game:

r.range(0.0f, 1.0f); // số thực trong [0, 1)
r.range(1, 6); // xúc xắc: 1 đến 6, gồm cả 6
r.chance(0.25f); // đúng với xác suất 25%
r.direction(); // vector độ dài 1, hướng bất kỳ
r.point_in(area); // điểm bất kỳ trong một hình chữ nhật
Bộ sinh số ngẫu nhiên PCG32: nhanh, chất lượng tốt, và lặp lại được: cùng hạt giống thì cùng dãy số.
Definition _random.h:13
vec2 point_in(rect r)
Điểm ngẫu nhiên trong hình chữ nhật.
Definition _random.h:80
vec2 direction()
Vector độ dài 1 theo một hướng ngẫu nhiên.
Definition _random.h:75
bool chance(f32 p)
Đúng với xác suất p.
Definition _random.h:71

njin::rng lặp lại được: cùng hạt giống thì cùng dãy số. Tự tạo một bộ riêng khi cần một dãy độc lập, ví dụ sinh bản đồ từ một mã số:

njin::rng level_rng(seed);