Có trường hợp toàn cầu / singletons hữu ích trong phát triển trò chơi không? [đóng cửa]


11

Tôi biết rằng việc có các biến toàn cục hoặc các lớp đơn lẻ tạo ra các trường hợp có thể khó kiểm tra / quản lý và tôi đã được sử dụng các mẫu đó trong mã nhưng thường thì bạn phải giao hàng.

Vì vậy, có trường hợp các biến toàn cầu hoặc singletons thực sự hữu ích trong phát triển trò chơi?

Câu trả lời:


30

Những điều này luôn có thể hữu ích . Cho dù đó là giải pháp đẹp nhất hay an toàn nhất là một vấn đề khác, nhưng tôi nghĩ việc phát triển trò chơi liên quan đến một mức độ thực dụng nhất định.


Rất gần với câu thần chú của tôi.
Chờ đợi Ólafur

15

Chắc chắn là một danh sách không đầy đủ, nhưng ở đây đi:

Nhược điểm

  • Quản lý trọn đời . Singletons và toàn cầu có thể muốn khởi động trước khi các hệ thống chính (ví dụ như heap) được khởi tạo, tùy thuộc vào cách bạn thiết lập chúng. Nếu bạn muốn xé chúng ra (chẳng hạn như hữu ích nếu bạn đang theo dõi rò rỉ), bạn phải cẩn thận về thứ tự xé hoặc bắt đầu đi vào những thứ như phượng hoàng . (Xem fiasco thứ tự khởi tạo tĩnh .)
  • Kiểm soát truy cập . Có nên kết xuất chỉ có quyền truy cập const vào dữ liệu trò chơi, trong khi cập nhật không phải là const? Điều này có thể khó thực thi hơn với singletons và toàn cầu.
  • Nhà nước . Như Kaj đã chỉ ra, việc phân rã và biến đổi các singleton thành chức năng hoàn toàn khó hơn trong một kiến ​​trúc không chia sẻ bộ nhớ. Nó cũng có ý nghĩa về hiệu suất tiềm năng đối với các loại hệ thống NUMA khác (truy cập bộ nhớ không cục bộ). Singletons thường đại diện cho trạng thái tập trung, và do đó là phản đề của các biến đổi được thực hiện dễ dàng hơn bởi độ tinh khiết.

Hoặc pro hoặc con

  • Đồng thời . Trong một môi trường đồng thời, những người độc thân có thể là một nỗi đau (bạn phải xem xét các vấn đề về cuộc đua / tái định cư dữ liệu) hoặc một phước lành (dễ tập trung hơn và lý do về việc khóa và quản lý tài nguyên). Sử dụng thông minh những thứ như lưu trữ cục bộ luồng có thể giảm thiểu phần nào các vấn đề tiềm ẩn, nhưng nó thường không phải là một vấn đề dễ dàng.
  • Codegen (tùy thuộc vào trình biên dịch, kiến ​​trúc, v.v.): Đối với triển khai đơn lẻ được tạo trước khi sử dụng, bạn có thể đánh giá một nhánh có điều kiện bổ sung cho mỗi lần truy cập. (Điều này có thể tăng lên.) Nhiều quả cầu được sử dụng trong một hàm có thể làm phình to nhóm chữ . Cách tiếp cận "struct of all global" có thể tiết kiệm không gian trong nhóm chữ: chỉ một mục nhập vào cơ sở của cấu trúc, và sau đó bù đắp được mã hóa trong hướng dẫn tải. Cuối cùng, nếu bạn đang tránh toàn cầu và singletons bằng mọi giá, bạn thường cần sử dụng ít nhất một bộ nhớ phụ (thanh ghi, ngăn xếp hoặc đống) để truyền con trỏ, tham chiếu hoặc thậm chí là các bản sao xung quanh.

Ưu

  • Đơn giản . Nếu bạn biết những nhược điểm được liệt kê ở trên không ảnh hưởng đến bạn nhiều (ví dụ: bạn đang làm việc trong môi trường một luồng cho một nền tảng cầm tay CPU), bạn sẽ tránh một số kiến ​​trúc (chẳng hạn như truyền đối số đã nói ở trên). Singletons và toàn cầu có thể dễ hiểu hơn đối với các lập trình viên ít kinh nghiệm hơn (mặc dù có thể dễ sử dụng chúng không chính xác).
  • Quản lý trọn đời (một lần nữa). Nếu bạn sử dụng "cấu trúc toàn cầu" hoặc một số cơ chế khác ngoài yêu cầu tạo theo yêu cầu đầu tiên, bạn có thể dễ dàng đọc và kiểm soát chi tiết về thứ tự khởi tạo và hủy. Bạn tự động hóa điều này ở một mức độ nào đó hoặc quản lý thủ công (phải quản lý nó có thể là pro hoặc con, tùy thuộc vào số lượng toàn cầu / singletons và phụ thuộc lẫn nhau của chúng).

Chúng tôi sử dụng một "cấu trúc của các singletons toàn cầu" rất nhiều trong các tiêu đề cầm tay của chúng tôi. Các tựa game trên PC và console có xu hướng dựa vào chúng ít hơn; chúng ta sẽ chuyển nhiều hơn sang kiến ​​trúc hướng sự kiện / nhắn tin. Điều đó đã được nói, các tiêu đề pc / console vẫn thường sử dụng một TextureManager trung tâm; vì nó thường bao bọc một tài nguyên được chia sẻ duy nhất (bộ nhớ kết cấu), điều này có ý nghĩa đối với chúng tôi.

Nếu bạn giữ API của mình tương đối sạch sẽ, có thể không quá khó để cấu trúc lại (hoặc thành!) Một mẫu đơn khi bạn cần ...


Danh sách tuyệt vời. Cá nhân tôi chỉ tìm thấy giá trị âm trong singletons và toàn cầu vì những vấn đề xuất phát từ trạng thái chia sẻ (có khả năng biến đổi). Tôi không thấy vấn đề "codegen" là một vấn đề; bạn cần phải truyền con trỏ qua các thanh ghi hoặc bạn cần thực hiện một số thao tác để có được nó, tôi nghĩ rằng nó thay đổi mã so với kích thước dữ liệu một cách rõ ràng, nhưng tổng sẽ giống nhau.
dash-tom-bang

Đúng, đó là codegen, điều duy nhất chúng ta thực sự thấy có sự khác biệt đáng kể là đi từ toàn cầu đến toàn cầu: nó thu nhỏ các nhóm chữ và do đó, kích thước phần .text, vài phần trăm. Đó là một điều rất tốt đẹp trên các hệ thống nhỏ. =) Lợi ích về hiệu suất của việc không sử dụng dcache khá nhiều đối với chữ nghĩa chỉ là một lợi ích phụ (mặc dù rất nhỏ trong hầu hết các trường hợp). Một lợi thế khác của việc di chuyển vào một bảng toàn cầu là khả năng thực sự dễ dàng di chuyển nó đến một phần đó cư trú trong bộ nhớ nhanh hơn ...
leander

4

Chúng có thể rất hữu ích, đặc biệt là trong quá trình tạo mẫu hoặc triển khai thử nghiệm, nhưng nói chung, chúng tôi thích chuyển các tham chiếu xung quanh cho các trình quản lý như các cấu trúc, theo cách đó bạn ít nhất có quyền kiểm soát nơi chúng được truy cập. Vấn đề lớn nhất với toàn cầu và singletons (theo tôi) là chúng không thân thiện với đa luồng và mã sử dụng chúng khó chuyển sang bộ nhớ không chia sẻ, như SPU.


2

Tôi có thể nói rằng bản thân thiết kế singleton hoàn toàn không hữu ích. Các biến toàn cục chắc chắn có thể hữu ích nhưng tôi muốn thấy chúng ẩn sau một giao diện được viết tốt để bạn không nhận thức được sự tồn tại của chúng. Với singletons bạn chắc chắn nhận thức được sự tồn tại của chúng.

Tôi thường sử dụng các biến toàn cục cho những thứ cần truy cập tất cả thông qua một công cụ. Công cụ hiệu suất của tôi là một ví dụ điển hình mà tôi sử dụng trên toàn bộ công cụ để gọi. Các cuộc gọi rất đơn giản; Thăm dò (), thăm dò () và thăm dò (). Truy cập thực sự của họ là một chút khó khăn hơn và sử dụng các biến toàn cầu cho một số thứ của họ.


2

Vấn đề chính với toàn cầu, và các singletons được triển khai kém là các lỗi xây dựng và giải cấu trúc tối nghĩa.

Vì vậy, nếu bạn làm việc với người nguyên thủy không có những vấn đề này hoặc nhận thức được vấn đề bằng con trỏ. Sau đó, chúng có thể được sử dụng một cách an toàn.

Quả cầu có vị trí của chúng, giống như gotos và không nên loại bỏ khỏi tầm tay mà thay vào đó được sử dụng cẩn thận.

Có một lời giải thích tốt về nó trong Hướng dẫn về Phong cách Google C ++


Tôi không đồng ý rằng đó là vấn đề chính; "không sử dụng toàn cầu" quay trở lại xa hơn các nhà xây dựng sự tồn tại. Tôi muốn nói có hai vấn đề lớn với họ. Đầu tiên, họ làm phức tạp lý luận về một chương trình. Nếu tôi có một chức năng truy cập toàn cầu, hành vi của chức năng đó không chỉ phụ thuộc vào các đối số của chính nó mà còn phụ thuộc vào tất cả các chức năng khác truy cập toàn cầu. Đó là rất nhiều để suy nghĩ về. Thứ hai, ngay cả khi bạn có thể giữ tất cả trong đầu (bạn không thể), chúng vẫn dẫn đến các vấn đề tương tranh phức tạp hơn.

@Joe từ khóa là "phụ thuộc." Một chức năng truy cập toàn cầu (hoặc singletons hoặc bất kỳ trạng thái chia sẻ nào khác) có sự phụ thuộc ngầm vào tất cả những điều đó. Sẽ dễ dàng hơn nhiều để lý do về một chút mã nếu tất cả các phụ thuộc của nó là rõ ràng, đó là những gì bạn nhận được khi danh sách đầy đủ các phụ thuộc được ghi lại trong danh sách đối số.
dash-tom-bang

1

Globals rất hữu ích trong khi tạo mẫu nhanh một hệ thống yêu cầu một số trạng thái giữa các lệnh gọi hàm. Khi bạn đã thiết lập được hệ thống hoạt động, hãy chuyển trạng thái thành một lớp và biến các hàm thành các phương thức của lớp đó.

Singletons rất hữu ích trong việc gây ra vấn đề cho chính bạn và những người khác. Bạn càng giới thiệu trạng thái toàn cầu, bạn sẽ càng gặp nhiều vấn đề với tính chính xác của mã, bảo trì, khả năng mở rộng, đồng thời, v.v. Đừng làm điều đó.


1

Tôi có xu hướng khuyên bạn nên sử dụng một số loại container DI / IoC với quản lý trọn đời tùy chỉnh thay vì singletons (ngay cả khi bạn sử dụng trình quản lý trọn đời "một thể hiện"). Ít nhất thì nó cũng dễ dàng trao đổi việc thực hiện để tạo điều kiện kiểm tra.


Đối với những người bạn chưa biết, DI là "Dependency Injection" và IoC là "Đảo ngược kiểm soát". Liên kết: martinfowler.com/articles/injection.html (Tôi đã đọc về những điều này trước đây nhưng chưa bao giờ thấy từ viết tắt, vì vậy tôi phải mất một chút tìm kiếm.) +1 để đề cập đến sự chuyển đổi tuyệt vời này.
leander

0

Nếu bạn muốn các tính năng tiết kiệm bộ nhớ của một singleton có lẽ bạn có thể thử mẫu thiết kế bay bổng?

http://en.wikipedia.org/wiki/Fly weight_potype

Đối với các vấn đề đa luồng được đề cập ở trên, nó khá đơn giản với một số tầm nhìn xa để thực hiện cơ chế khóa cho các tài nguyên có thể được chia sẻ giữa các luồng. http://en.wikipedia.org/wiki/Read/write_lock_potype


0

Singletons là một cách tuyệt vời để lưu trữ trạng thái chia sẻ trong nguyên mẫu ban đầu.

Chúng không phải là viên đạn bạc và có một số vấn đề, nhưng chúng là một mẫu rất hữu ích cho các trạng thái UI / Logic nhất định.

Ví dụ: trong iOS, bạn sử dụng singletons để nhận [UIApplication sharedApplication], trong cocos2d, bạn có thể sử dụng nó để có được các tham chiếu đến một số đối tượng nhất định như [CCNotifying sharedManager] và cá nhân tôi thường bắt đầu với một singleton [Game sharedGame] lưu trữ trạng thái được chia sẻ giữa rất nhiều thành phần khác nhau.


0

Ồ, điều này thật thú vị với tôi, vì cá nhân tôi chưa bao giờ gặp vấn đề với mẫu đơn. Dự án hiện tại của tôi là một công cụ trò chơi C ++ cho Nintendo DS và tôi đang triển khai rất nhiều tiện ích truy cập phần cứng (ví dụ: VRAM Banks, Wifi, hai công cụ đồ họa) như các phiên bản đơn lẻ, vì chúng dự định bao bọc toàn cầu C các chức năng trong thư viện cơ bản.


Câu hỏi của tôi sẽ là sau đó, tại sao lại sử dụng singletons? Tôi thấy rằng mọi người thích bọc API bằng các singletons hoặc các lớp chỉ bao gồm các hàm tĩnh, nhưng tại sao lại bận tâm với lớp này? Tôi có vẻ dễ dàng hơn khi chỉ có các lệnh gọi hàm (được gói theo ý muốn), nhưng sau đó nội bộ để chúng truy cập trạng thái "toàn cầu". Ví dụ: Log :: GetInstance () -> LogError (...) cũng có thể là LogError (...) truy cập nội bộ bất kỳ trạng thái toàn cầu nào mà không yêu cầu mã máy khách biết về nó.
dash-tom-bang

0

Chỉ khi bạn có một mục chỉ có một bộ điều khiển nhưng được xử lý bởi một số mô-đun.

Chẳng hạn như, ví dụ, giao diện chuột. Hoặc giao diện cần điều khiển. Hoặc máy nghe nhạc. Hoặc máy nghe nhạc. Hoặc màn hình. Hoặc trình quản lý lưu tập tin.


-1

Quả cầu nhanh hơn nhiều! Vì vậy, nó sẽ phù hợp hoàn hảo cho một ứng dụng chuyên sâu về hiệu năng, giống như một trò chơi.

Singletons là một công cụ toàn cầu tốt hơn, IMO, và do đó là công cụ phù hợp.

Sử dụng nó một cách tiết kiệm!


Nhanh hơn nhiều so với những gì ? Các quả cầu sẽ xuất hiện trong một số trang bộ nhớ ngẫu nhiên nếu chúng là một con trỏ và một số trang bộ nhớ cố định nhưng có thể ở xa nếu chúng là một giá trị. Trình biên dịch không thể sử dụng bất kỳ tối ưu hóa lổ nhìn trộm hữu ích nào trên chúng. Theo bất kỳ biện pháp nào tôi có thể nghĩ ra, toàn cầu là chậm .

Nhanh hơn getters và setters và chuyển tham khảo xung quanh. Nhưng không nhiều lắm. Nó giúp giảm kích thước, vì vậy nó sẽ giúp trong một số hệ thống. Tôi có thể phóng đại khi tôi nói 'nhiều', nhưng tôi rất hoài nghi về bất cứ ai nói rằng bạn không nên sử dụng một cái gì đó. Bạn chỉ cần sử dụng ý thức chung của bạn. Xét cho cùng, singletons không có gì khác hơn là một thành viên lớp tĩnh và đó là những người toàn cầu.
jacmoe

Nhưng để giải quyết bất kỳ vấn đề đồng bộ hóa nào với toàn cầu, bạn cần getters / setters cho chúng, vì vậy điều đó không thực sự phù hợp. Vấn đề với (và lợi ích hiếm có của) trạng thái toàn cầu là trạng thái toàn cầu, không phải bất kỳ mối quan tâm nào về tốc độ hoặc (hiệu quả) các khía cạnh cú pháp của giao diện.
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.