Những cạm bẫy điển hình khi viết trò chơi với Ngôn ngữ được quản lý như C # là gì? [đóng cửa]


66

Những cạm bẫy nào bạn gặp phải khi viết trò chơi cho PC với Ngôn ngữ được quản lý như C # và bạn đã giải quyết chúng như thế nào?


Câu hỏi này được hỏi tốt hơn trên Stack Overflow, vì có rất ít về nó dành riêng cho các trò chơi.
Chris Garrett

31
@Chris: Rất không đồng ý: Câu hỏi đặc biệt đề cập đến các trò chơi! Các vấn đề bạn gặp phải khi bạn phải cập nhật mỗi 16ms rất khác với những gì bạn gặp phải trong hầu hết các ứng dụng trên máy tính để bàn.
Andrew Russell

Câu hỏi khá không rõ ràng. Java và C # đủ khác nhau để chỉ lời khuyên rất chung chung được áp dụng cho cả hai. Tất cả các câu trả lời cho đến nay là C #. Ngoài ra nền tảng mục tiêu không được đề cập - lời khuyên tốt có thể khác nhau tùy thuộc vào thiết bị (ví dụ: lập trình cho điện thoại di động, zune, xbox, khác với lập trình cho máy tính). Nó thậm chí còn là một câu hỏi đủ rộng để ai đó trả lời rằng chính ngôn ngữ được quản lý là "cạm bẫy".
paulecoyote

@paulecoyote: Tôi đổi thành câu hỏi chỉ hỏi về C #. Ngoài ra, vì không có nền tảng cụ thể nào được đề cập, đó là về PC.
Michael Klement

@Michael giả định nền tảng là một giả định nguy hiểm vì chúng khác nhau rất nhiều trong việc triển khai, sẽ rất tốt khi đề cập đến Windows một cách cụ thể và bỏ hoàn toàn "như" và "Java".
paulecoyote

Câu trả lời:


69

Tôi không biết nhiều về Java, vì vậy đây là từ quan điểm của một nhà phát triển .net.

Cái lớn nhất cho đến nay là rác. Trình thu gom rác .NET trên Windows thực hiện công việc tuyệt vời và bạn có thể thoát khỏi mà không cần cho bé ngồi phần lớn. Trên Xbox / Windows Phone 7, đó là một vấn đề khác. Nếu bạn nhận được quầy hàng cứ sau vài khung hình, bộ sưu tập rác có thể gây ra sự cố cho bạn. Tại thời điểm này, nó kích hoạt sau mỗi phân bổ 1MB.

Dưới đây là một số mẹo để xử lý rác. Bạn không cần phải lo lắng về hầu hết những điều này trong thực tế, nhưng chúng có thể có ích vào một ngày nào đó.

  • Vẽ nội dung của GC.GetTotalMemory()màn hình. Điều này cung cấp cho bạn một xấp xỉ số lượng byte được phân bổ mà trò chơi của bạn sử dụng. Nếu nó hầu như không di chuyển, bạn đang làm tốt. Nếu nó tăng nhanh, bạn có vấn đề.
  • Cố gắng phân bổ tất cả các đối tượng heap của bạn lên phía trước. Nếu bạn không phân bổ mọi thứ trước khi trò chơi bắt đầu, mỗi khi bạn đạt được một meg phân bổ, bạn sẽ bị đình trệ. Không phân bổ, không có bộ sưu tập. Đơn giản vậy thôi.
  • Sau khi tải, gọi GC.Collect(). Nếu bạn biết hầu hết các khoản phân bổ lớn của mình đều nằm ngoài dự đoán, thật tuyệt khi cho hệ thống biết.
  • KHÔNG GỌI GC.Collect()mọi khung hình. Nó có vẻ như là một ý tưởng tốt, giữ lại rác của bạn và tất cả những thứ đó, nhưng hãy nhớ rằng chỉ có điều tồi tệ hơn một bộ sưu tập rác là trên bộ sưu tập rác.
  • Tìm nơi rác của bạn đến từ. Có một số nguyên nhân phổ biến như nối chuỗi thay vì sử dụng StringBuilder(hãy cẩn thận, StringBuilderkhông phải là viên đạn ma thuật và vẫn có thể gây ra sự phân bổ! Điều này có nghĩa là các thao tác đơn giản như thêm số vào cuối chuỗi có thể tạo ra lượng rác đáng ngạc nhiên) hoặc sử dụng foreachcác vòng lặp trên các bộ sưu tập sử dụng IEnumerablegiao diện cũng có thể tạo rác mà bạn không biết (ví dụ: foreach (EffectPass pass in effect.CurrentTechnique.Passes)là một phổ biến)
  • Sử dụng các công cụ như trình lược tả bộ nhớ CLR để tìm ra nơi bộ nhớ được phân bổ. Có rất nhiều hướng dẫn về cách sử dụng công cụ này.
  • Khi bạn biết nơi phân bổ của mình trong khi chơi trò chơi, hãy xem liệu bạn có thể sử dụng các thủ thuật như gộp các đối tượng để giảm số lượng phân bổ đó không.
  • Nếu vẫn thất bại, làm cho bộ sưu tập của bạn chạy nhanh hơn! GC trên khung nhỏ gọn tuân theo mọi tham chiếu trong mã của bạn để tìm ra những đối tượng nào không được sử dụng nữa. Tái cấu trúc mã của bạn sử dụng ít tài liệu tham khảo!
  • Hãy nhớ sử dụng IDisposabletrên các lớp có chứa tài nguyên không được quản lý. Bạn có thể sử dụng những thứ này để dọn sạch bộ nhớ, GC không thể tự giải phóng.

Điều khác cần suy nghĩ là hiệu suất điểm nổi. Mặc dù .NET JITer thực hiện một số lượng tối ưu hóa cụ thể của bộ xử lý, nhưng nó không thể sử dụng SSE hoặc bất kỳ bộ hướng dẫn SIMD nào khác để tăng tốc các phép toán dấu phẩy động của bạn. Điều này có thể gây ra sự chênh lệch tốc độ khá lớn giữa C ++ và C # cho các trò chơi. Nếu bạn sử dụng đơn âm, họ có một số thư viện toán học SIMD đặc biệt mà bạn có thể tận dụng.


Hoan toan Đông y. Việc triển khai trình thu gom rác trên các nền tảng "nhỏ hơn" dường như là rác hoàn chỉnh.
Krisc

Tuy nhiên, bài viết hay liên quan đến tuyên bố của bạn về "phân bổ các đối tượng heap lên phía trước" Tôi khuyên bạn nên đọc Sự thật về các loại giá trị
Justin

Điểm tuyệt vời ở đó, khi bạn tối ưu hóa mã thực sự đáng để hiểu nền tảng mà bạn đang cố gắng tối ưu hóa. Đó không thực sự là một nhận xét về các loại giá trị / loại tham chiếu, bất kỳ đối tượng tồn tại lâu nào không có khả năng nằm trên ngăn xếp. Nó thực sự là về việc đảm bảo bạn nhận được càng nhiều phân bổ của mình được thực hiện trong thời gian tải càng tốt, bạn không gặp phải rào cản 1mb kỳ diệu đó trong khi chơi trò chơi. Tất cả các triển khai .net mà xna nhắm mục tiêu cũng có một đảm bảo gọn gàng, các đối tượng được phân bổ gần nhau theo thời gian sẽ ở gần trong không gian, có thể tốt cho sự hoàn hảo.
Cubed2D

Cũng có vẻ như tôi đã quên đề cập rằng khung nhỏ gọn hiện tại trên xbox bị dị ứng với các cuộc gọi phương thức nội tuyến. Giữ cái đó cho trường hợp hoàn hảo nhất trong trường hợp xấu nhất của bạn, sử dụng các phiên bản ref của các phương pháp toán học trông có vẻ xấu xí như nó vốn có!
Cubed2D

10

Một cạm bẫy hiệu suất đỉnh cao là không xem xét bộ thu gom rác trong thiết kế / phát triển của trò chơi. Sản xuất quá nhiều rác thải có thể dẫn đến "trục trặc" trong trò chơi, điều này xảy ra khi GC chạy trong một thời gian dài đáng kể.

Đối với C #, sử dụng các đối tượng giá trị và câu lệnh "sử dụng" có thể giảm bớt áp lực từ GC.


3
Ngoài ra, bạn có thể yêu cầu Trình thu gom rác chạy một cách rõ ràng nếu bạn vừa hoàn thành một vòng lặp nặng phân bổ / miễn phí hoặc nếu bạn thấy bạn có chu kỳ dự phòng.
BarrettJ

16
Các usingtuyên bố không có gì để làm với thu gom rác thải! Nó dành cho IDisposablecác đối tượng - vốn là để giải phóng các tài nguyên không được quản lý (ví dụ: những tài nguyên không được xử lý bởi trình thu gom rác ).
Andrew Russell

9

Tôi muốn nói rằng vấn đề lớn nhất mà tôi gặp phải khi viết trò chơi trong C # là thiếu thư viện phong nha. Hầu hết tôi đã tìm thấy là các cổng trực tiếp, nhưng không đầy đủ hoặc bao bọc trong thư viện C ++ phải chịu một hình phạt hiệu năng nặng cho việc sắp xếp theo thứ tự. (Tôi đang nói cụ thể về MOgre và Axiom cho thư viện OGRE và BulletSharp cho thư viện vật lý Bullet)

Các ngôn ngữ được quản lý (khác với Phiên dịch - cả Java và C # không thực sự được giải thích nữa) có thể nhanh như ngôn ngữ bản địa nếu bạn hiểu rõ về những gì thực sự làm cho chúng chậm (thu thập dữ liệu, thu gom rác). Vấn đề thực sự, tôi nghĩ, là các nhà phát triển thư viện chưa nhận ra điều đó.


Đó là một điểm tốt về C # và Java được quản lý thay vì diễn giải ... đã chỉnh sửa câu hỏi của tôi để làm cho chính xác hơn :)
Michael Klement

2
Nói chung, Marshalling là một nút cổ chai hiệu năng, nhưng nó cũng giúp nhận thức được các loại có thể bị xóa - các loại có thể được ánh xạ trực tiếp vào bộ nhớ không được quản lý mà không có tác động hiệu suất đáng kể. msdn.microsoft.com/en-us/l Library / 75dwhxf7.aspx
Sean Edwards

8

Giống như những người khác đã nói, tạm dừng bộ sưu tập GC là vấn đề lớn nhất. Sử dụng nhóm đối tượng là một giải pháp điển hình.


4

C # và Java không được giải thích. Chúng được biên dịch thành mã byte trung gian, sau JIT , trở nên nhanh như mã gốc (hoặc gần đủ để không đáng kể)

Cạm bẫy lớn nhất mà tôi tìm thấy là giải phóng tài nguyên ảnh hưởng trực tiếp đến trải nghiệm người dùng. Các ngôn ngữ này không tự động hỗ trợ hoàn thiện xác định như C ++, điều mà nếu bạn không mong đợi nó có thể dẫn đến những thứ như lưới nổi về cảnh sau khi bạn nghĩ rằng chúng bị phá hủy. (C # hoàn thành quyết toán xác định thông qua IDis Dùng , tôi không chắc Java làm gì.)

Ngoài ra, các ngôn ngữ được quản lý thực sự có khả năng xử lý loại hiệu suất mà các trò chơi yêu cầu nhiều hơn so với tín dụng. Mã được quản lý tốt được viết nhanh hơn nhiều so với mã gốc được viết kém.


Vâng, cảm ơn bạn đã lưu ý, đã sửa chữa điều được giải thích / quản lý;) Ngoài ra, điểm tốt với các lưới nổi về cảnh. Không nghĩ về điều đó khi nghĩ về các vấn đề của GC ...
Michael Klement

1
IDisposablecho phép dọn dẹp xác định các tài nguyên quan trọng và không được quản lý theo thời gian, nhưng không ảnh hưởng trực tiếp đến việc hoàn thiện hoặc bộ thu gom rác.
Sam Harwell

4

Cả Java và C # đều không được giải thích. Cả hai đều được biên dịch thành mã máy riêng.

Vấn đề lớn nhất với cả hai và trò chơi là phải viết mã theo cách mà chúng không bao giờ thu gom rác trong khi chơi trò chơi. Số lượng hoops bạn phải nhảy qua để hoàn thành gần như vượt xa lợi ích của việc sử dụng chúng ở nơi đầu tiên. Hầu hết các tính năng làm cho những ngôn ngữ đó trở nên thú vị để sử dụng cho lập trình ứng dụng hoặc máy chủ phải được tránh để lập trình trò chơi nếu không bạn sẽ bị tạm dừng lâu trong khi chơi trò chơi khi chúng tắt và thu gom rác.


Việc thu gom rác có thể được xử lý, ít nhất là trong C #, khá hợp lý, vì vậy tôi sẽ không nói rằng đó là một công cụ giải quyết. Với phân luồng thích hợp và nhận thức về trạng thái chương trình, bạn có thể tránh các vấn đề về hiệu suất. Đó vẫn là một điều khác mà bạn phải suy nghĩ và nó làm cho ngôn ngữ được quản lý trở nên ít quản lý hơn.
Karantza

4
Điều đáng chú ý là trong .NET 4, trình thu gom rác hỗ trợ bộ sưu tập nền cho cả ba thế hệ đối tượng. Điều này sẽ giảm thiểu hiệu quả tác động của việc thu gom rác trong các trò chơi. Liên kết có liên quan: geekswithbloss.net/sdorman/archive/2008/11/07/NH
Mike Strobel

Người thu gom rác đang phát triển, và theo ghi nhận của Mike Strobel, một số đã được sản xuất gần như loại bỏ cạm bẫy này.
Sam Harwell

2
Cả C # và Java đều không được biên dịch thành mã máy gốc. C # biên dịch thành MSIL và Java thành mã byte. Bộ sưu tập rác sẽ không cung cấp bất kỳ tạm dừng "dài" nào, nhưng nó có thể cho "trục trặc".
Cloudanger

2

Một cạm bẫy lớn mà tôi thấy từ việc tạo ra các trò chơi với các ngôn ngữ như thế này (hoặc sử dụng các công cụ như XNA, TorqueX engine, v.v.) là bạn sẽ khó tìm được một đội ngũ những người giỏi có chuyên môn cần thiết để tạo ra một trò chơi tương đương với những gì sẽ khá dễ dàng để tìm thấy mọi người trong C ++ và OpenGL / DirectX.

Ngành công nghiệp phát triển trò chơi vẫn còn rất nặng về C ++ vì hầu hết các công cụ và đường ống được sử dụng để đẩy ra các trò chơi nhỏ lớn hoặc được đánh bóng tốt đều được viết bằng C ++, và theo như tôi biết thì TẤT CẢ các bộ dụng cụ phát triển chính thức bạn có thể get for XBox, PS3 và Wii chỉ được phát hành với khả năng tương thích với C ++ (bộ công cụ XBox có thể được quản lý nhiều hơn hiện nay, có ai biết nhiều hơn không?)

Nếu bạn muốn phát triển trò chơi dành cho bảng điều khiển ngay bây giờ, bạn có khá nhiều XNA và C # trên XBox và sau đó chỉ trong một phần phụ của thư viện trò chơi có tên XBox Live Indie Games. Một số người chiến thắng trong các cuộc thi, v.v. được chọn để chuyển trò chơi của họ sang XBox Live Arcade thực sự. Khác với kế hoạch làm một trò chơi cho PC.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.