- Tôi phát triển mọi trò chơi trong dự án cá nhân bằng ‘C thuần’, một lựa chọn hiện nay khá hiếm
- Tiêu chí cốt lõi khi chọn ngôn ngữ là độ tin cậy, tính di động và khả năng duy trì lâu dài, không bị ràng buộc vào một hệ điều hành hay nền tảng cụ thể nào
- Tôi coi trọng sự đơn giản và tốc độ biên dịch nhanh, cùng với kiểm tra kiểu nghiêm ngặt và hệ thống cảnh báo mạnh mẽ
- Tôi đã cân nhắc các ngôn ngữ thay thế như C++·C#·Java·Go·Haxe, nhưng kết luận rằng chúng không phù hợp vì độ phức tạp, GC, sự áp đặt của OOP, v.v.
- C nguy hiểm nhưng đơn giản và nhanh, và nhờ khả năng hỗ trợ nền tảng rộng cùng hệ sinh thái thư viện vững chắc, nó vẫn là lựa chọn tối ưu
Tiêu chí chọn ngôn ngữ
- Điều kiện bắt buộc là độ tin cậy và sự ổn định, để tôi không phải lãng phí thời gian vào những lỗi mà mình không tạo ra
- Trước đây tôi từng phát triển game dựa trên Flash, nhưng do Flash suy tàn, tôi muốn tập trung làm game mới thay vì chuyển chúng sang nền tảng mới
- Tôi coi trọng tính di động và hỗ trợ thư viện phổ dụng để không bị phụ thuộc vào một hệ điều hành cụ thể và vẫn mở ra khả năng phát triển cho console
- Những điều kiện mong muốn bao gồm cú pháp đơn giản và cấu trúc dễ nhớ
- Tôi muốn tránh việc liên tục phải tra cứu các API hay tính năng ngôn ngữ phức tạp
- Tôi muốn giảm lỗi bằng kiểm tra kiểu nghiêm ngặt, cảnh báo mạnh và phân tích tĩnh, đồng thời dễ tìm vấn đề hơn nhờ trình gỡ lỗi tốt và các công cụ phân tích động
- Tôi đánh giá tốc độ biên dịch là cực kỳ quan trọng
- Thời gian chờ dài làm đứt mạch tập trung và giảm năng suất
- Tôi hoài nghi về lập trình hướng đối tượng (OOP), và thích cách tách dữ liệu với mã để xử lý tùy theo tình huống hơn
Đánh giá các ngôn ngữ thay thế chính
- C++
- Dù vẫn là tiêu chuẩn trong phát triển game, tôi không hài lòng vì độ phức tạp và tốc độ biên dịch chậm
- Nó mang lại hiệu năng cao và nhiều tính năng, nhưng cũng có quá nhiều tính năng tôi không muốn và cái giá phải trả cho độ phức tạp là rất lớn
- C# và Java
- Chúng dài dòng và phức tạp, đồng thời giảm mức độ tự do vì cấu trúc quá nặng về OOP
- Là ngôn ngữ bậc cao, chúng che giấu độ phức tạp, nhưng không ngăn được các vấn đề cốt lõi
- Go
- Tôi đánh giá tích cực nó như một bản diễn giải hiện đại của C, nhưng garbage collection stop-the-world không phù hợp với phát triển game
- Ngoài ra còn có lo ngại về sự thiếu hụt thư viện cho game và tính bền vững lâu dài
- JavaScript
- Những thay đổi nhanh trong môi trường phát triển web và cái kết của Flash khiến nó tạo cảm giác thiếu ổn định
- Tôi cho rằng cú pháp lỏng lẻo khiến nó không phù hợp để viết phần mềm quy mô lớn
- Haxe
- Tôi đánh giá nó đầy hứa hẹn khi phát triển web, nhưng vẫn lo ngại về tính bền vững vì đây là một ngôn ngữ còn tương đối mới
- Tự phát triển ngôn ngữ riêng
- Đây là một ý tưởng hấp dẫn, nhưng tôi cho rằng không thực tế vì phải từ bỏ các thư viện hiện có và gánh nặng duy trì khả năng tương thích
Vì sao chọn C
- Đây là một ngôn ngữ nguy hiểm nhưng đáng tin cậy; nhờ cấu trúc đơn giản, nếu dùng cẩn thận thì vẫn rất ổn định
- Tác giả ví nó như một “con dao sắc”: khó sử dụng nhưng khi đã thành thạo thì là một công cụ rất mạnh
- Tốc độ biên dịch rất nhanh và có thể chạy trên gần như mọi nền tảng
- Quá trình port cũng tương đối đơn giản, và về lâu dài khả năng tồn tại của nó cũng cao
- Hỗ trợ thư viện và tooling rất mạnh, đồng thời vẫn được duy trì ổn định
- Về cá nhân, tôi đã có nhiều kinh nghiệm với mã ‘C thuần’, nên rất quen thuộc với nó
- Tôi không khuyến nghị người khác dùng C; đây là một lựa chọn tối ưu cho sở thích cá nhân và cách làm việc của riêng tôi
1 bình luận
Ý kiến trên Hacker News
Tôi chủ yếu viết code theo phong cách C, chỉ dùng tính năng C++ khi thực sự cần
Vì vậy nhìn thoáng qua đôi khi còn giống code Rust
Những người nói họ viết game bằng C thường ghét các tính năng của C++, nhưng rồi cuối cùng lại tự triển khai giao diện ảo hoặc viết những khối
switchkhổng lồ để làm việc tương tựNếu không muốn thì cứ đừng dùng các tính năng có sẵn trong ngôn ngữ, chứ than phiền về việc chúng tồn tại thì tôi thấy không có nhiều ý nghĩa
C++ không chậm khi biên dịch nếu bạn không lạm dụng template
Theo thời gian, thành viên thay đổi, leader thay đổi, và tập hợp các tính năng được sử dụng sẽ ngày càng phình ra
Một khi đã mở rộng thì cực kỳ khó thu gọn lại
Ngoài ra bạn cũng có thể buộc phải dùng thư viện sử dụng những tính năng mình không muốn
Ví dụ, tôi ghét các lời gọi ngầm định như destructor hay operator overloading, nhưng nếu thư viện dùng thì cuối cùng mình vẫn phải theo
switchcòn đọc đượcMột trong những điểm tệ nhất của C++ là có quá nhiều đoạn mã ẩn được sinh ra tự động
Dù phần lớn code chỉ xử lý các kiểu cụ thể như Goose hay Duck, mọi object vẫn phải mang theo con trỏ vtable
Trong khi đó Rust chỉ dùng vtable ở những chỗ thực sự cần, nên tiết kiệm bộ nhớ hơn
Lập trình viên C thì tự triển khai đúng phần mình cần, nên ít bị trói buộc bởi cấu trúc mà ngôn ngữ áp đặt
Chỉ cần include mỗi
<vector>thôi cũng kéo theo hàng chục nghìn dòng codeVì thế nếu không dùng standard library thì lại nổ ra tranh cãi kiểu “sao phải phát minh lại bánh xe”
Mà những cuộc tranh cãi như vậy lặp đi lặp lại thì quay về C lại dễ chịu hơn hẳn
Tôi cũng chuyển sang C từ khoảng năm 2017, và đến giờ cứ mỗi lần dùng thư viện C++ là lại thấy mệt
Ngoại lệ thì Dear ImGui khá ổn, nhưng ngay cả với nó tôi vẫn thích C binding hơn
Đổi phần mở rộng sang
.cthì thời gian biên dịch giảm một nửaTôi thích sự thô ráp và đơn giản của C, chỉ là ghét preprocessor
Vì thế Zig đúng là như món quà trời cho — đơn giản hơn C nhưng lại có thiết kế ngôn ngữ chính xác hơn
Ví dụ, Zig phân biệt giữa con trỏ đơn và con trỏ mảng
Có thể import thư viện C rồi biến chúng thành thứ dễ dùng hơn, rất hữu ích trong phát triển game
Phần lớn thư viện C++ cũng đi kèm header C
zig-gamedev có rất nhiều thư viện C được Zig hóa như vậy
Thay vì preprocessor, tính năng comptime của Zig tốt hơn nhiều, và về bản chất nó chỉ là “code Zig chạy ở thời điểm biên dịch”
Tôi cũng đồng cảm với quan điểm của tác giả
Lý do lớn nhất khiến tôi thích một ngôn ngữ là sự đơn giản
Vì thế tôi ưu tiên những ngôn ngữ như C, Go, Odin và Zig
Nhưng những ngôn ngữ xử lý độ phức tạp cần thiết một cách thanh lịch cũng rất quan trọng
Với code mạng cần memory safety, concurrency và các mẫu hàm chức năng, Rust là lựa chọn tự nhiên
Còn với code đơn giản và nhanh như game indie thì C hay Odin lại rất hợp
Odin cho cảm giác như kết hợp sự đơn giản của Go với hiệu năng của C nên tôi khuyên dùng
Làm game với Raylib cũng dễ
Tôi tò mò không biết bạn có nghĩ Odin phù hợp với vị trí đó hơn Zig không
Bài gốc nói rằng Go thiếu hỗ trợ thư viện game, nhưng đó có vẻ là một bài viết từ khoảng năm 2015
Giờ có thể tình hình đã khác
Ở đó cũng xem được ảnh chụp màn hình các game được tạo hồi ấy
Ví dụ Knossu có phong cách 3D khá độc đáo
Làm game bằng C vào năm 2026 có thể hơi điên rồ, nhưng tôi cũng đang làm vậy
Chẳng hạn Chrysalis được tôi phát triển bằng C (dùng GLFW3 và FMOD)
Nó dựa trên idTech3, mà hệ thống combat thì tinh chỉnh đến mức chỉ một thay đổi nhỏ cũng phá hỏng toàn bộ cân bằng
Thậm chí chỉ thêm một
i++thôi cũng làm lệch timingVì thế chúng tôi vẫn giữ nguyên compiler từ 22 năm trước
Có các fork đã hiện đại hóa, nhưng chúng làm mất cảm giác nguyên bản
idTech3 đúng là engine thể hiện tinh túy của C
Hàng nghìn game đã được viết bằng C, và các graphics API như OpenGL, Vulkan, DX cũng đều dựa trên C
Nên chuyện đó chẳng có gì lạ
Phần lớn game engine cũng được viết bằng C/C++
Console thì khác nhau theo từng thế hệ
Khác với lập trình Linux, phát triển game đã xoay quanh C++ hơn 30 năm nay
Về cơ bản thì tôi là người rất yêu C
Tôi đã dùng nó rất ổn suốt hàng chục năm, nhưng khi quản lý code C theo nhóm thì nỗi đau tăng lên rõ rệt
Ngoài ra tốc độ phát triển cũng chậm hơn so với các ngôn ngữ hiện đại
Dù vậy, sức hút từ sự đơn giản của nó vẫn còn nguyên
Theo trải nghiệm của tôi thì C còn đỡ đau hơn C++
Điều đó phần nào làm suy yếu lập luận về giới hạn cộng tác của C
Nói rằng “không ai làm game bằng C” là cường điệu
Giờ nó không còn là dòng chính, nhưng vẫn có rất nhiều người làm game bằng C
Bạn chỉ là ngoại lệ, còn câu đó vẫn đúng về mặt thống kê
Tôi thích C
Tôi có thể kiểm soát hoàn toàn quản lý bộ nhớ, và đổi lại là hành vi có thể dự đoán được
Trong các môi trường yêu cầu cấp phát sẵn như bộ quy tắc MISRA, nó đặc biệt hữu ích
Nó cũng phù hợp để trực tiếp xử lý exception hay lỗi ở mức phần cứng
Viết unit test cũng dễ
C có rào cản tiếp cận thấp, nhưng kiến thức về quản lý bộ nhớ là bắt buộc
Là một lập trình viên Java, khi phải nhanh chóng tạo connector trong tình huống chỉ có
.hvà.so, tôi đã chọn C thay vì C++Nếu C là một con dao sắc, thì C++ giống như một cột dao đang xoay — lơ là là bị thương
Chỉ có điều xử lý chuỗi trong C quá đau khổ, đến mức tôi muốn mượn hệ thống string của C++