VFX & Particle System
Unity có hai hệ thống hạt — Particle System (CPU, linh hoạt) và VFX Graph (GPU, hàng triệu hạt) — dùng cho lửa, khói, mưa, tia lửa và hiệu ứng phép thuật.
Hãy tưởng tượng...
Particle System giống như máy bắn pháo hoa thủ công — bạn thiết lập tốc độ bắn, góc bắn, màu sắc từng tia, và CPU tính toán từng hạt một. Chính xác nhưng chậm với số lượng lớn. VFX Graph là máy pháo hoa công nghiệp — toàn bộ tính toán chạy song song trên GPU, có thể bắn hàng triệu hạt cùng lúc nhưng cần GPU mạnh và URP/HDRP. Chọn cái nào phụ thuộc vào: số hạt, platform target, và độ phức tạp hành vi.
Khái niệm chi tiết
Particle System (còn gọi là Shuriken) là component gắn vào GameObject, tạo và cập nhật hạt trên CPU. Mỗi hạt có thuộc tính: vị trí, vận tốc, màu, kích thước, thời gian sống. Hệ thống module (Emission, Shape, Velocity, Color over Lifetime, Force, Collision…) cho phép tùy chỉnh rất chi tiết. Phù hợp cho: lửa nhỏ, mưa, tuyết, hiệu ứng hit, smoke — từ vài chục đến vài nghìn hạt.
VFX Graph (Visual Effect Graph) chạy hoàn toàn trên GPU compute shaders. Sử dụng giao diện node graph tương tự Shader Graph để xây dựng logic cập nhật hạt (Initialize, Update, Output). Hỗ trợ hàng triệu hạt với hiệu năng tốt. Yêu cầu: URP hoặc HDRP, GPU với Compute Shader support (hầu hết GPU từ 2015+), không hỗ trợ Android/iOS thế hệ cũ.
Kiến trúc VFX Graph chia hạt thành: Spawn (tạo hạt theo tốc độ/burst), Initialize (đặt thuộc tính ban đầu), Update (cập nhật mỗi frame: vật lý, màu sắc, kích thước), và Output (cách vẽ: quad, mesh, ribbon). Các context này kết nối với nhau qua luồng dữ liệu, giống pipeline rendering.
Về hiệu năng: Particle System tốt cho mobile và console nếu giữ dưới 1000 hạt
cùng lúc. Hạn chế bằng maxParticles và Stop Action = Disable sau khi
phát. VFX Graph scale tốt hơn nhưng cần profile kỹ trên platform target.
So sánh Particle System vs VFX Graph
| Tiêu chí | Particle System | VFX Graph |
|---|---|---|
| Xử lý | CPU | GPU (Compute Shader) |
| Số hạt tối ưu | 100 – 10,000 | 10,000 – 10,000,000+ |
| Pipeline | Built-in, URP, HDRP | URP, HDRP only |
| Mobile | ✅ Tốt | ⚠️ Hạn chế |
| Interface | Inspector module | Node graph editor |
| Script API | Đầy đủ C# API | Limited (SetFloat, SetTexture) |
| Dùng khi | Lửa, mưa, hit FX, smoke | Galaxy, fluid, crowd, destruction |
Hướng dẫn thực hành (Particle System)
Tạo Particle System
GameObject → Effects → Particle System, hoặc Add Component → Particle System vào GameObject hiện tại.
Cấu hình Main module
Duration, Looping, Start Lifetime (bao lâu 1 hạt sống), Start Speed, Start Size, Start Color — cơ bản nhất.
Chọn hình dạng phát (Shape)
Shape module: Cone (lửa), Sphere (explosion), Box (mưa từ trên), Circle (ring). Điều chỉnh Radius và Arc.
Bật Color over Lifetime
Thêm gradient: đỏ → cam → vàng → transparent để mô phỏng lửa tắt dần.
Play từ code khi cần
ps.Play() / ps.Stop() / ps.Emit(count) — hoặc dùng EmitParams để burst tức thì.
Trình mô phỏng tương tác
Điều chỉnh các thông số Particle System và nhấn Burst / preset để xem hiệu ứng.
Ví dụ Code
Cơ bảnPlay/Stop Particle System và emit burst khi cần (ví dụ: hit effect khi đạn trúng).
using UnityEngine;
public class HitEffect : MonoBehaviour
{
public ParticleSystem sparks;
public ParticleSystem smoke;
// Gọi khi đạn va chạm — PlayOneShot dùng prefab tại vị trí cụ thể
public static void PlayAt(Vector3 pos, Vector3 normal,
GameObject fxPrefab)
{
GameObject fx = Instantiate(fxPrefab, pos,
Quaternion.LookRotation(normal));
Destroy(fx, 2f); // Tự xoá sau 2 giây
}
// Burst emit: phát 30 hạt ngay lập tức, không cần looping
public void BurstEmit()
{
ParticleSystem.EmitParams ep = new ParticleSystem.EmitParams();
ep.position = transform.position;
sparks.Emit(ep, 30);
smoke.Play();
}
// Thay đổi màu hạt tại runtime qua Main module
public void SetColor(Color color)
{
var main = sparks.main;
main.startColor = new ParticleSystem.MinMaxGradient(color);
}
}Ví dụ Code
Nâng caoĐiều khiển VFX Graph từ C# — set properties và trigger event bằng ExposedProperty.
using UnityEngine;
using UnityEngine.VFX;
public class VFXController : MonoBehaviour
{
public VisualEffect vfx;
// Property IDs — khớp với tên Exposed Property trong VFX Graph
static readonly int SpawnRateID = Shader.PropertyToID("SpawnRate");
static readonly int ColorID = Shader.PropertyToID("BaseColor");
static readonly int ExplodeID = Shader.PropertyToID("OnExplode");
void Start()
{
vfx.SetFloat(SpawnRateID, 50f);
vfx.SetVector4(ColorID, new Vector4(1f, 0.5f, 0f, 1f));
}
public void Explode()
{
// SendEvent kích hoạt event trong VFX Graph (Burst spawn)
vfx.SendEvent(ExplodeID);
}
public void SetIntensity(float t)
{
// Điều chỉnh spawn rate 0 → 500 theo intensity
vfx.SetFloat(SpawnRateID, Mathf.Lerp(0f, 500f, t));
}
}📌 Ghi nhớ nhanh
- ▸Particle System: CPU, mọi platform, <10k hạt
- ▸VFX Graph: GPU, URP/HDRP only, triệu hạt
- ▸Stop Action = Disable để tái sử dụng (object pooling)
- ▸ps.main.startColor cần gán lại struct để có hiệu lực
- ▸VFX Graph: SendEvent() kích hoạt Burst context
⚠️ Lỗi thường gặp
❌ Spawn nhiều PS prefab mà không Destroy
Hàng nghìn Particle System tích luỹ → lag nặng
✅ Object Pool PS, hoặc Destroy(fx, lifetime) sau khi play xong
❌ Chỉnh ps.main.startColor.color trực tiếp
Struct copy — thay đổi không áp dụng
✅ var main = ps.main; main.startColor = new MinMaxGradient(c);