Cơ chế cơ bản trong 'suất lợi nhuận www' của Unity3D Game Engine


14

Trong công cụ trò chơi Unity3D, một chuỗi mã phổ biến để nhận dữ liệu từ xa là:

WWW www = new WWW("http://remote.com/data/location/with/texture.png");
yield return www;

Cơ chế cơ bản ở đây là gì?

Tôi biết chúng tôi sử dụng cơ chế năng suất để cho phép khung tiếp theo được xử lý, trong khi quá trình tải xuống đang được hoàn tất. Nhưng những gì đang xảy ra dưới mui xe khi chúng ta làm điều đó yield return www?

Phương thức nào đang được gọi (nếu có, trên lớp WWW)? Là Unity sử dụng chủ đề? Có phải lớp Unity "phía trên" đang nắm giữ cá thể www và đang làm gì đó không?

BIÊN TẬP:

  • Câu hỏi này là cụ thể về nội bộ Unity3D. Tôi không quan tâm đến giải thích về cách yieldhoạt động của câu lệnh trong C #. Thay vào đó, tôi đang tìm kiếm một cái nhìn bên trong về cách Unity xử lý các công trình này, ví dụ, cho phép WWW tải xuống một phần dữ liệu theo cách phân tán trên một số khung.

1
Lưu ý rằng sử dụng yield returncho các hoạt động không đồng bộ là một hack. Trong chương trình C # "thực", bạn sẽ sử dụng một chương trình Tasknày. Unity có thể không sử dụng chúng vì nó đã được tạo trước .Net 4.0, khi Taskđược giới thiệu.
BlueRaja - Daniel Pflughoeft

Câu trả lời:


8

Đây là từ khóa năng suất C # đang hoạt động - nó không làm gì đặc biệt với wwwđối tượng, mà nó có nghĩa là một cái gì đó đặc biệt cho phương thức mà nó chứa trong đó. Cụ thể từ khóa này chỉ có thể được sử dụng trong một phương thức trả về một IEnumerable(hoặc IEnumerator) và được sử dụng để cho biết đối tượng nào sẽ được "trả về" bởi điều tra viên khi MoveNext được gọi.

Nó hoạt động vì trình biên dịch chuyển đổi toàn bộ phương thức thành một lớp riêng biệt thực hiện IEnumerable(hoặc IEnumerator) bằng cách sử dụng máy trạng thái - kết quả thực tế là phần thân của phương thức không được thực thi cho đến khi có ai đó liệt kê giá trị trả về. Điều này sẽ làm việc với bất kỳ loại nào, hoàn toàn không có gì đặc biệt WWW, thay vào đó là phương thức chứa đặc biệt.

Hãy xem Hậu trường của từ khóa năng suất C # để hiểu rõ hơn về loại mã mà trình biên dịch C # tạo ra, hoặc chỉ tự mình thử nghiệm và kiểm tra mã bằng cách sử dụng thứ gì đó như IL Spy


Cập nhật: Để làm rõ

  • Khi Unity gọi một coroutine có chứa một yield returncâu lệnh, tất cả những gì xảy ra là một điều tra viên được trả về - không có phần thân phương thức nào được thực thi tại thời điểm này
  • Để có được phần thân phương thức thực thi, Unity phải gọi MoveNextiterator để lấy giá trị đầu tiên trong chuỗi. Điều này khiến phương thức thực thi đến yeild returncâu lệnh đầu tiên , tại đó người gọi tiếp tục lại (và có lẽ Unity tiếp tục hiển thị phần còn lại của khung)
  • Theo tôi hiểu, Unity bình thường sau đó tiếp tục gọi MoveNextphương thức trên iterator một lần mỗi khung hình tiếp theo, khiến phương thức thực hiện lại cho yield returncâu lệnh tiếp theo một lần cho mỗi khung hình, cho đến khi kết thúc phương thức hoặc yield breakcâu lệnh (đạt kết thúc chuỗi)

Bit chỉ đặc biệt ở đây (và trong một vài các trường hợp khác ) là Unity không tiến iterator đặc biệt này frame kế tiếp, thay vào đó nó chỉ tiến iterator (gây phương pháp để tiếp tục thực hiện) khi quá trình download hoàn thành. Mặc dù dường như có một lớp YieldInXD cơ sở có lẽ chứa một cơ chế chung để báo hiệu cho Unity khi một trình lặp được nâng cao, nhưng WWWlớp này dường như không kế thừa từ lớp này vì vậy tôi chỉ có thể giả sử rằng có một trường hợp đặc biệt cho lớp này trong công cụ Unity.

Nói rõ hơn - yieldtừ khóa không làm gì đặc biệt cho WWWlớp, thay vào đó là cách xử lý đặc biệt mà Unity dành cho các thành viên của bảng liệt kê trả lại gây ra hành vi này.


Cập nhật lần thứ hai: Đối với cơ chế WWWsử dụng để tải xuống các trang web không đồng bộ, nó có thể sử dụng Phương thức httpWebRequest.BeginGetResponse , trong đó sẽ sử dụng IO không đồng bộ hoặc thay vào đó, nó có thể sử dụng các luồng (hoặc tạo một luồng chuyên dụng) hoặc bằng cách sử dụng một luồng xử lý).


5
Trên thực tế, trong Unity, một cái gì đó đặc biệt xảy ra với một WWWđối tượng khi nó được mang lại, xem WWWtài liệu tham khảo .
Eric

9

yielddường như chủ yếu được sử dụng trong Unity trong bối cảnh coroutine. Để đọc thêm về coroutines và lý do tại sao họ sử dụng C #, yieldtôi đề xuất bài viết trên blog này: Unity3D coroutines một cách chi tiết . Hầu hết các nghiên cứu trong câu trả lời này đến từ bài báo đó.

Quân đoàn trong Unity được sử dụng để gói gọn các nhiệm vụ:

  1. Có thể mất nhiều thời gian hơn một khung hình được hiển thị (do đó gây ra sự chậm lại) và
  2. Có thể được thực hiện tách biệt với vòng lặp trò chơi (vì kết quả không cần có sẵn cho khung hiện tại).

Ví dụ về các loại nhiệm vụ này là tính toán tìm đường dẫn hoặc, như trường hợp trong câu hỏi của bạn, lấy dữ liệu từ một trang web.

Để trả lời các câu hỏi con của bạn (theo thứ tự được sửa đổi một chút):

Phương thức nào đang được gọi (nếu có, trên lớp WWW)? Có phải lớp Unity "phía trên" đang nắm giữ cá thể www và đang làm gì đó không?

WWWLớp học của Unity được thiết kế để mang lại từ một coroutine. Theo các nhận xét về bài viết trên blog được liên kết ở trên, khối mã đầu cơ ("lớp trên") về YieldInstructions thực sự có chứa một công tắc cũng kiểm tra các WWWs mang lại . Mã này sau đó đảm bảo coroutine sẽ tự động kết thúc khi quá trình tải xuống được thực hiện, như được mô tả trong WWWtài liệu tham khảo .

Là Unity sử dụng chủ đề?

Trong trường hợp này, để tải xuống dữ liệu "mà không chặn phần còn lại của trò chơi": có, rất có thể. (Và luồng chắc chắn sử dụng để giải nén các dữ liệu tải về, với bằng chứng WWW.threadPriority.)


đẹp! Tôi thấy bình luận altdevblogaday.com/2011/07/07/unity3d-coroutines-in-detail/ gợi và dường như WWW được đối xử đặc biệt. Vì vậy, tôi muốn biết làm thế nào điều này được thực hiện, để cho phép tải xuống được thực hiện trên một số khung hình, mà không cần sử dụng chủ đề?
thyandrecardoso

Điểm tốt! Tôi cho rằng phải có một số luồng đang diễn ra ở cấp độ đó, tôi sẽ chỉnh sửa câu trả lời của mình để phản ánh điều đó.
Eric

1
@thyandrecardoso Tôi đoán nó sử dụng HttpWebRequest.BeginGetResponse hoặc tương tự, tuy nhiên bạn có thể dịch ngược cụm để xác nhận điều này nếu nó thực sự quan trọng với bạn.
Justin

bây giờ, tôi thực sự chỉ chờ đợi "lời giải thích tốt nhất" ... sẽ thật tuyệt nếu bất cứ ai trong nhóm phát triển Unity đưa ra "câu trả lời đúng" 8 -) ... cuối cùng, tôi nghĩ rằng việc triển khai thực sự sẽ không khác xa với những cái đã được đưa ra ở đây ... Tôi không có nhu cầu thực sự dịch ngược lắp ráp và chắc chắn biết tất cả điều này. Nhưng tôi có thể thử nó sau :)
thyandrecardoso

7

Thật không may, WWW được triển khai bên trong dưới dạng mã gốc, có nghĩa là chúng ta không thể xem mã. Từ thử nghiệm tôi có thể nói rằng

  1. WWWkhông có nguồn gốc từ YieldInstruction, vì vậy bất cứ xảy ra khi bạn yieldnó phải được xử lý bằng cách đặc biệt hợp cụ mã.
  2. Tôi chưa bao giờ thấy bất kỳ sự khác biệt giữa

    yield return www;

    while(!www.isDone)
        yield return null;

    Tôi nghĩ đó là cách hợp lý nhất để thực hiện nó, và có lẽ đó là những gì đang diễn ra dưới mui xe. Nhưng tôi không biết chắc chắn.

  3. Unity không bắt đầu một chủ đề mới để tải xuống, ít nhất là trên một số nền tảng (iOS, trình phát web). Hoặc nếu có, nó đặt WWW.isDonetrên luồng chính. Tôi biết điều này bởi vì mã này:

    while(!www.isDone)
        Thread.Sleep(500);

    không hoạt động.

Tôi không nghĩ bạn có thể có câu trả lời cụ thể hơn trừ khi ai đó có quyền truy cập vào mã nguồn của Unity3d đến đây.


Vâng Những hiểu biết thực sự tốt đẹp! Cảm ơn! Mặc dù không khởi chạy một chủ đề, nhưng điều có thể xảy ra nhất là WWW (hoặc lớp động cơ) bằng cách sử dụng HttpWebRequest.BeginGetResponse (hoặc một cái gì đó tương tự) ... phải không? Một cái gì đó hoàn toàn không đồng bộ phải xảy ra theo bất kỳ cách nào ... quá trình tải xuống không thể bị "tạm dừng".
thyandrecardoso

** Ý tôi không thể là "tạm dừng" giữa các khung.
thyandrecardoso

2

Vì Unity3D sử dụng C # làm công cụ viết kịch bản của họ, tôi cho rằng từ khóa năng suất tiêu chuẩn được tích hợp trong C #. Về cơ bản, điều đó có nghĩa là nó trả về giá trị của www để bạn có thể tiếp tục trong khi lần lặp tiếp theo, nó sẽ trả về giá trị tiếp theo, v.v ... Yield về cơ bản tạo ra một máy trạng thái và trình lặp trong nền.


Vâng, tôi nghĩ rằng tôi có những khái niệm cơ bản về từ khóa suất. Tuy nhiên, điều gì đang xảy ra trong quá trình xây dựng WWW cho phép "cỗ máy nhà nước" đó? Ý tôi là, "lợi nhuận www" dường như không gọi bất cứ thứ gì trong lớp WWW ... Khi bạn nói "có nghĩa là nó trả về giá trị của www rồi", giá trị cá thể www là gì? Trường hợp đó sẽ làm gì trong "lần lặp" tiếp theo?
thyandrecardoso

1
Trong Unity coroutines, mang lại WWW là một trường hợp đặc biệt, xem WWWtài liệu tham khảo . Hơn nữa, tôi không chắc chắn yieldtạo ra bất cứ điều gì. Bối cảnh lặp được tạo bằng cách thực hiện IEnumerablehoặc sử dụng nó làm kiểu trả về. "Máy nhà nước" dường như cũng tắt. Chắc chắn, có nhà nước, nhưng đó không phải là một tài sản đủ, phải không? Có lẽ bạn có thể giải thích về điều đó.
Eric

1
Dưới đây là một tài liệu tham khảo tốt về hành vi C # bình thường. Shadowcoding.blogspot.nl/2009/01/yield-and-c-state-machine.html . Yield tạo ra rất nhiều mã để theo dõi vị trí của nó trong iterator. Về trường hợp đặc biệt của Unity mang lại WWW, tôi không biết nhưng theo các tài liệu thì nó không liên quan gì đến từ khóa C # thông thường, điều đó rất khó hiểu, họ chỉ có thể biến nó thành một phương thức không đồng bộ.
Roy T.
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.